Files
PaperClipAI/doc
Dotta 92452524f4 fix(server): recover authenticated empty retention checkpoints (#19417)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Ephemeral server enrollment must preserve existing task files.
> - Archived task directories can depend on encrypted retention
checkpoints.
> - A missing object blocks migration even when its authenticated
contents were exactly empty.
> - Metadata counters alone cannot prove that no saved files existed.
> - This change verifies the exact candidate against the original
encrypted key envelope before normal migration publication.
> - The benefit is lossless migration of this bounded case without
resetting task history or weakening ordinary restore.

## Linked Issues or Issue Description

**What happened?**
Migration stops at an archived empty directory when its retained
encrypted object is missing. The original encrypted key envelope still
authenticates the payload hash, byte length, company, workspace,
checkpoint ID, and generation.

**Expected behavior**
Administrative migration can reconstruct those exact original bytes when
storage confirms absence and every authenticated check passes. It must
keep missing nonempty, expired, corrupt, or ambiguous sources blocked.

**Steps to reproduce**
1. Capture and encrypt an empty task directory.
2. Retain its reference and encrypted key envelope.
3. Remove the old directory and storage object.
4. Run the normal remote workspace migration.

**Paperclip version or commit**
The regression fails before this change at `205ffcb606`. Related
preservation work is in #19396, #19402, and #19409. No duplicate open
fix was found.

**Deployment mode**
Administrative migration to external workspace storage. Tests use real
encryption, real filesystem storage, and disposable PostgreSQL.

## What Changed

- Let the encryption owner verify a candidate against the existing
scoped encrypted key envelope. Normal ciphertext validation keeps its
previous order and checks.
- Share the task-directory encoder with an exact empty payload
constructor.
- Add a default-off loader option for confirmed missing empty
checkpoints. Only the administrative migration caller enables it.
- Keep the original checkpoint reference and object unchanged. Publish
the recovered bytes through the existing snapshot and source-fence
owners.
- Reject forged counters, wrong identities, missing nonempty data,
present corruption, unreadable present objects, unknown absence, and
expiry.

## Verification

- Final affected run: all 238 tests pass across five crypto, snapshot,
retention, and migration suites. Full repository typecheck and build
pass; server typecheck and full build also pass after the cancellation
repair.
- RED then GREEN: exact missing-empty recovery and cancellation of a
stalled HEAD through the real storage-service boundary. Cancellation
retains the caller's original abort reason.
- Disposable PostgreSQL migration publishes a cold-restorable empty
archive. Archived status, task status, task binding, original reference,
and the absent original source stay unchanged. Replay keeps one
canonical snapshot.
- Negative cases reject forged empty counters, an authenticated
directory or file, every foreign identity field, expiry, present
corruption, unreadable present objects, unknown absence, and missing
nonempty data. Ordinary restore keeps its previous failure behavior.
- Initial verification retained one new fixture-path failure after 145
passes, and a typecheck rejected the fixture's widened schema literal.
Correct the fixture to use the returned staging path and its explicit
reference type. The final complete affected run passes.
- The new HEAD request runs only after a failed read with the
administrative option enabled. It uses the existing cancellation
contract and adds no polling or background work.
- Final-head full CI passes; review is 5/5 with no open findings. This
PR does not claim deployed enrollment or a live migration result.

## Risks

Recovery requires the original valid encrypted key envelope and an exact
format match. A format change or unavailable key continues to block
migration. Existing ciphertext hash, size, authentication, and ordinary
restore behavior remain enforced. This change has no schema,
provider-session, API, or UI contract changes.

## Model Used

OpenAI GPT-6 in Codex, with code editing, tool use, and test execution.
The exact runtime model 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
#` 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 references)
- [x] My branch name describes the change 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
2026-11-06 00:50:28 -06:00
..
…
…
…
…