Files
PaperClipAI/doc
DottaandPaperclip 06c5bc471d fix(skills): unblock retention for legacy run selections (#16852)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent runs can select saved skill versions.
> - The server keeps those files in a cache that must protect active and
unresolved runs.
> - Older runs can lack the newer pin map even when their saved
execution input records the full selection.
> - Finished interrupted runs can also keep a company blocked after all
execution and cleanup have settled.
> - This pull request uses existing selection evidence and recognizes
settled interruptions so safe cache expiration can resume.

## Linked Issues or Issue Description

Related: #16433.

**What happened?**

Skill cache retention reports `unknown_run_pins` for a whole company
when it finds a finished interrupted run without pins, or an older
native run whose complete selection is stored only in its execution
input. Old cache directories cannot expire.

**Expected behavior**

Release pins from fully settled interruptions. Preserve exact versions
from valid saved native selections. Continue to block cleanup when
selection evidence is missing, invalid, or belongs to another run.

**Steps to reproduce**

1. Materialize an old, restorable skill version and let its idle grace
period expire.
2. Add a finished interrupted run with settled ownership and cleanup, or
an unresolved native run with a valid saved execution input but no
modern pin map.
3. Run the retention service. Before this change, it skips the company
with `unknown_run_pins`.

## What Changed

- Treat `interrupted` as terminal only when the existing finish,
controller, finalization, and environment cleanup checks allow it.
- For native runs with no modern pin-map field, parse the saved
execution input with the existing recovery parser. Validate its digests
and company, run, agent, and issue binding.
- Keep captured version IDs protected. Unversioned skills use separate
captured asset bundles. Do not replace a malformed modern map or write
any recovery records.
- Add database regressions and document the fallback. Remove an unused
status list that no longer represented the actual eligibility query.

## Verification

- Passed 41 focused tests: `skill-version-cache.test.ts`,
`skill-version-retention.test.ts`, and
`skill-version-retention-config.test.ts`.
- The tests perform real PostgreSQL reads, directory eviction, and
restoration from saved bytes with upstream unavailable. They cover
leases, unfinished cleanup, invalid digests, invalid versions, and
mismatched identities.
- Passed `node scripts/check-module-boundaries.mjs` and `git diff
--check`.
- A read-only evaluation of the candidate selector against existing
legacy data cleared the unknown-pin blocker while retaining exact
version pins.
- Passed `pnpm -r --workspace-concurrency=2 typecheck` and `pnpm build`.
The full remote CI suite passed, including all server, workspace,
Runner, and end-to-end shards. The full local test command remains in
progress; its focused cache suites passed as recorded above.
- Greptile reviewed this head at 5/5 with no actionable comments or
unresolved threads.
- Initial local database attempts did not run the cases: sandbox access
prevented a listener, then an install with scripts disabled lacked
PostgreSQL library symlinks. After running the package's symlink setup,
the binary started and all focused tests passed.

## Risks

The main risk is releasing a cache still in use. The existing ownership
and cleanup checks remain in force. The native fallback applies only
when the modern field is absent and the saved input passes the same
parser used for recovery. Invalid or missing evidence still blocks the
company. Current versions and explicit agent pins stay protected. Saved
version rows and runtime asset bundles are not deleted. There is no
migration.

The selector reads execution inputs only for legacy native runs that
need the fallback. Normal runs still load only their pin maps and
binding columns. A representative read-only selector check completed in
98 ms; this is a sample, not a latency guarantee.

## Model Used

OpenAI Codex, GPT-6 family, with tool-assisted coding and verification.

## 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 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-19 10:10:49 -05:00
..
2026-02-16 14:25:00 -06:00