Files
PaperClipAI/doc/plans
DottaandPaperclip 980775e0e9 fix(server): schedule inbound voice history retention by deadline (#17583)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Incoming voice calls leave history after provider cleanup is
confirmed.
> - The chat maintenance tick repeatedly deletes old admission history.
> - Each eligible row already contains the dates needed for its
retention deadline.
> - This pull request schedules that deadline and wakes on committed
changes.
> - Idle retention stops querying while the existing seven-day policy
remains enforced.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Inbound voice history retention. This PR follows merged #17580, which
owns physical call cleanup, and now targets master. Call reports remain
a separate migration.

**Current behavior**

Voice maintenance scans admission history repeatedly. It deletes
confirmed, terminal, unbound admissions when both expiry and last update
are at least seven days old.

**Proposed behavior**

Load existing retention deadlines at startup. Wake from the existing
admission transaction notifications. Delete at the later timestamp plus
seven days. Lock and recheck current eligibility before deletion.

**Reason and benefit**

Configured idle endpoints and future history do not cause recurring
retention SQL. Failed deletion retains a retry. Restart reconstructs the
same saved deadlines.

## What Changed

- Add a voice-owned retention worker using the existing scoped deadline
helper and coordinator. Yield after 100 history rows per scope refresh
and reset reconstruction on registration. Keep complete one-shot
inventory reads. No table, field, or migration is added.
- Extend `notifyVoiceInbound` to notify retention before the same
committed writes that already notify cleanup. Callers need no new
recovery wrapper.
- Preserve confirmation, terminal-state, session, task, and seven-day
eligibility conditions. Round PostgreSQL submillisecond deadlines up.
- Lock and recheck the current row before deletion. New bindings or
moved dates invalidate stale candidates.
- Remove history pruning from recurring voice maintenance. Keep the
explicit one-shot diagnostic path. Leave call reports for their own
slice.
- Document producer ownership, retry/drain behavior, tests, and external
wake limitations.

## Verification

Published head: `53f2fafcb64d104887eb4455c292b48e6dd10253`. It includes
the normal merge of actual landed master
`71b448b9cb841e6fa18fe883585430c5081245da` after cleanup #17580. That
ancestry integration at `768c650365` preserved the complete binary diff
of all 13 reviewed retention paths. This follow-up changes only the
existing cleanup fairness test and its failure ledger, bringing the
final scope to 14 paths; all production source is unchanged from that
integration. All unrelated master changes, cleanup repair, migration
`0396_tan_paladin.sql`, and gateway clock repair are preserved. Database
files, shared worker, workflows and lockfile match actual master. The
earlier 282-case/type/build evidence below belongs to unchanged
retention source at its stated head; current repair acceptance is 37
distinct complete cleanup and retention lifecycle cases. All 58
current-head checks are now clean, including the Canary Dry Run;
Greptile is exact-head 5/5 with no new actionable issues. The current
Ponytail turn stopped without a verdict, which is an authorized
nonblocking state. The earlier failing CI remains preserved below.

- **282 distinct targeted cases pass**: all 127 voice integration cases
and all 155 cases in ten complete retention, cleanup, recovery,
company-removal, shared-worker, startup and hostname suites. These
include 13 retention and 14 cleanup lifecycle cases. Voice integration,
parent recovery and company removal start on separate empty databases
and migrate themselves. Retention uses its dedicated database after the
setup repair; cleanup uses its already-migrated dedicated database.
- Ponytail's prior exact-head 4/5 review found configuration-driven
history rescans. Five real connection saves selected 1,025 rows over 205
future admissions before repair. Retention now receives only admission
hints and startup reconstruction; the same saves select zero admission
rows and retain the original deadline. Cleanup still receives its full
configuration and authority scopes. Restrictive binding foreign keys
prevent implicit unbinding outside the admission-write boundary.
- Successful deletion now returns its confirmed result without reloading
the deleted row. A real SQL-count case and a guarded negative probe
cover that path. Changed or not-yet-due history still reloads current
eligibility and its deadline.
- Server typecheck, build and module-boundary checks pass. No schema,
driver, shared scheduler contract or provider protocol changes are added
by this slice.
- A real 105-row future-history case failed before the reader repair
because startup selected 105 rows rather than yielding at 100. The
existing paged shared-worker contract now bounds each scope refresh. The
same-owner restart case reconstructs every deadline and deletes all 105
eligible rows at the original deadline. It verifies SQL quiet before
that deadline and after settlement. Removing only cursor reset fails at
five rows instead of 100 after restart; guarded source was restored
before final acceptance.
- Existing cases retain the later-of-two-timestamps seven-day policy,
submillisecond rounding, current-state confirmation and binding checks,
concurrent binding lock, rollback/outer commit, first write during
drain, failure retry and shutdown. Real signed ingress, cleanup
confirmation and maintenance-lane removal remain covered by the complete
voice suite.
- The fresh retention fixture first failed on missing schema and now
applies the application migrator. The initial parent merge duplicated a
test import and failed to parse before any voice case ran. Both failures
are retained in the audit; the corrected complete suites pass.
- Prior head `daca270a99` had a failed server (4/12) check, run
40557790710/job 122386307340. Its actual log reports a runner shutdown
and cancellation before completion. This is incomplete acceptance, not a
pass or a proven product flake. New-head CI must pass independently.
- [Current-parent acceptance and failure
ledger](https://github.com/paperclipai/paperclip/blob/68b62b6d13f28ac5f17f35b98adf5488752f9b97/doc/plans/2026-10-25-voice-inbound-retention-sleep-audit.md#current-parent-paging-and-restart-acceptance-october-28)
records the exact scope, commands, counts and preserved failures.
Earlier 218/133/134-case and full-workspace results belong to their
stated heads. No new full local repository suite, live provider, Cloud
wake, host/database suspension or savings claim is made.
- [Notification review repair and current 282-case
evidence](https://github.com/paperclipai/paperclip/blob/68b62b6d13f28ac5f17f35b98adf5488752f9b97/doc/plans/2026-10-25-voice-inbound-retention-sleep-audit.md#configuration-rescan-review-repair-october-28)
records the new source. The prior head had green CI and a 4/5 Ponytail
review; neither is accepted for this new commit. Fresh exact-head
Greptile 5/5 and all CI remain required. Completed actual failing
Ponytail findings must be repaired; a stopped or paused turn without a
verdict is not a merge blocker under the user’s authorization. An active
Ponytail review waits at most 30 minutes from its original request
(25-minute monitor cutoff).

- **Preserved current-master CI failures:** run `40697141057` failed
server shard 7/12 in cleanup’s fairness test and Canary Dry Run on an
npm `rollup-4.66.0.tgz` 404. A fixed final admission UUID reproduces the
same fairness assertion: the second company was sometimes beyond the
first 100-row page, so expecting it among the first 25 calls was
invalid. The corrected test controls time and proves service within the
next 25-call batch after discovery, all 105 successful calls before the
failed provider’s saved retry, exactly one retry at its deadline, and
idle SQL after settlement. Bypassing company rotation fails the
corrected assertion; the shared helper was restored byte-for-byte. Both
complete cleanup (24) and retention (13) suites pass after restoration.
An intermediate 23/24 run incorrectly demanded idle before
reconstruction finished; the audit preserves that failure and its
corrected pending-intent wait. No production behavior, timeouts,
dependencies or workflow gates changed. The exact npm metadata and
tarball now return 200; new-head canary CI must pass independently.
Prior Greptile/Ponytail 5/5 on `768c650365` are stale for this new head.
[Full CI repair
ledger](https://github.com/paperclipai/paperclip/blob/53f2fafcb64d104887eb4455c292b48e6dd10253/doc/plans/2026-10-25-voice-inbound-retention-sleep-audit.md#current-master-ci-repair-october-28).

## Risks

- Supported admission writers must use the existing transaction
notification boundary. Independent SQL writers and additional processes
need a separate wake contract.
- Retention never deletes unconfirmed cleanup or session/task-bound
history. Those records remain with their owners.
- The in-process deadline does not wake stopped compute. This PR does
not extend Cloud alarms or relax sleep eligibility, and does not claim
deployed host/database suspension.
- Call-report reconciliation and other chat maintenance still need
separate migrations.

## Model Used

OpenAI Codex with reasoning, shell, and code-editing tools. The exact
model version 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 for this new head
- [x] Greptile is 5/5 on this new head with no unresolved actionable
findings
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-28 08:57:58 -05:00
..