Files
PaperClipAI/server
DottaandPaperclip 302ada90df fix(workspace): refresh source admission proofs on import resume (#20394)
## Thinking Path

> - Paperclip preserves agent work and recovery obligations across
server replacement.
> - Online workspace imports keep durable operation and snapshot
journals for interrupted work.
> - A recovery admission fingerprint hashes complete run, finalizer, and
lease rows.
> - Accounting updates can change that hash while every saved recovery
reference remains identical.
> - Resume currently treats that expired admission observation as
immutable source identity and refuses the existing journal.
> - This pull request refreshes only that observation after exact
identity checks and finalization fencing, preserving the original
archive and journal.

## Linked Issues or Issue Description

**What happened?**

An interrupted import whose saved recovery sources have no changing
online recovery hold can fail on its next attempt with
`remote_workspace_migration_source_changed`. A change to a run's
accounting fields changes `onlineProof.fingerprint`, even when all eight
saved recovery records have the same typed references and ownership
fields.

**Expected behavior**

For source-only admission drift, resume must compare every saved
recovery source field exactly, except the supported ephemeral admission
fingerprint. It must inspect the current source again, acquire the
existing finalization fence, and retain strict full-proof checks through
publication. It must keep the original operation, snapshot, archive,
nested-child scope, and cohort identity.

**Steps to reproduce**

1. Save a journal for an online import with eight recovery sources and
no active online recovery holds. Interrupt after real authenticated
restore of its uploaded snapshot.
2. Change only accounting fields on source 6's saved run.
3. Resume the same journal. The unchanged importer refuses it. The fixed
importer preserves the original identities and commits after fresh
guarded inspection.

**Paperclip version or commit**

Reproduced on master `9c634e42e449e91f3935cf59036981852f94a65e`.

**Deployment mode**

Online administrative imports with PostgreSQL. Related preservation
work: https://github.com/paperclipai/paperclip/pull/20311 and
https://github.com/paperclipai/paperclip/pull/20370. The independent
readonly parallelism work in
https://github.com/paperclipai/paperclip/pull/20387 changes different
loops.

## What Changed

- Compare the complete `legacyRecoverySources` records at resume,
neutralizing only a valid fingerprint under the supported proof schema.
Preserve every other field and refuse unsupported proof versions by
name.
- Reuse the current canonical full proofs. Persist them with an
operation metadata compare-and-swap only after the existing finalization
lock and strict reinspection succeed.
- Keep strict full-proof checks within each attempt and before
publication. Preserve all journal, snapshot, archive, nested-child, and
cohort identities.
- Add focused real PostgreSQL regressions in the existing online-source
fixture, including exactly eight saved sources and repeat-apply
idempotence.

## Verification

- Genuine baseline RED: singleton and cohort resume both fail after
accounting-only changes on the unchanged product.
- `pnpm exec vitest run
server/src/services/remote-workspace-online-source.test.ts -t 'eight
saved recovery|changed saved recovery|recovery accounting changes|saved
recovery proof'`: 8 passed.
- Controls refuse changed valid references, a matched run/lease
allocation change, competing ownership, and unsupported proof versions.
They also prove that bookkeeping changes within an admitted attempt
still refuse and a busy finalization fence prevents proof refresh.
- Successful resumes preserve the operation/snapshot IDs, cipher SHA,
manifest digest, byte size, nested scope, and cohort. Repeat apply
creates no duplicate operation or snapshot.
- Server layered typecheck, module boundaries, and whitespace checks
passed. All applicable exact-head CI passed (including typecheck, build,
all test shards and aggregate verify); hosted acceptance remains owned
by the deployment workstream. Greptile re-reviewed the narrowed scope at
5/5. Ponytail scored 4/5, recommending a regression for the unchanged
hold-bearing refusal; the maintainer explicitly kept that separate
behavior outside this source-only fix.

## Risks

This changes only source-proof resume admission. `onlineRecoveryHolds`
and their action/intent fingerprints remain compared exactly, as on the
baseline: a hold-bearing journal whose hold proof changes still refuses.
Supporting that separate case would require preserving and validating
the hold owner’s complete intent facts; this PR deliberately retains
that safety check. Bookkeeping that changes during an active attempt
still causes the existing strict publication checks to refuse that
attempt. Older unsupported proof shapes remain refused. The metadata
update uses the existing company/workspace/operation identity,
running/unfinished status, and metadata compare-and-swap. No schema,
provider, or storage format changes are included.

## Model Used

OpenAI Codex, based on GPT-6, with tool use and code execution. The
exact deployment identifier and context-window size are not exposed in
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-11-09 17:05:17 -06:00
..