Files
PaperClipAI/doc/plans
DottaandPaperclip 4a89456bb3 fix(server): schedule Teams consent expiry from saved deadlines (#17590)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Teams file delivery saves consent and transfer state in the
database.
> - Unused consent must expire even when no new message arrives.
> - The maintenance loop currently checks this state every 30 seconds.
> - This PR schedules each existing consent deadline and wakes on
committed changes.
> - Empty or settled consent work stops querying while expiry remains
recoverable.

## Linked Issues or Issue Description

**What existing behavior does this improve?**
Teams unused file-consent expiry. This is slice 26 of the
recurring-server migration stack, following merged #17586 and targeting
master.

**Current behavior**
The Teams maintenance callback scans for expired unused consent and
abandoned transport attempts together, even when consent work is empty.

**Proposed behavior**
The Teams owner reconstructs unused consent at startup and reloads one
transfer after a committed write. The shared scheduler runs its saved
expiry. Abandoned attempts keep their current recovery until a separate
slice migrates them.

**Reason and benefit**
Remove unnecessary consent queries without losing expiry, atomic
publication projection, or recovery after restart.

**Breaking changes**
No schema or API change. The explicit combined recovery method remains
available. Cloud sleep eligibility and external alarms remain unchanged.

## What Changed

- Add a Teams-owned scoped deadline worker for the three unused consent
phases. Registered reconstruction yields after one 100-row page per
scope refresh and resets cursors on registration; the explicit
diagnostic inventory still returns every eligible row.
- Signal before insertion and the existing versioned update inside the
owning transaction. Nested writes wait for outer settlement.
- Extract the existing locked per-transfer recovery operation. Preserve
authority checks, atomic projection, bounded lock waits and
unknown-outcome quarantine.
- Remove unused consent from periodic maintenance selection and
execution. Keep abandoned attempts as a separate obligation.
- Register the worker through existing app lifecycle gates. Add
lifecycle, producer, production wiring and precision regressions.
- Document supported writers, remaining sleep limits and the failure
ledger in `doc/idle-teams-consent-expiry.md` and its linked audit.

## Verification

Published head: `9e84cfab1f795279c8cec413a946631f1d781bd2`. This
follow-up changes only the inherited voice-retention test and its CI
failure ledger; all production behavior is unchanged from `36f43922`. It
normally merges actual landed master/report parent
`8049fbf51d0701ca6b242e4b1198e5449f7661b2`. The verified 12-path Teams
patch was byte-identical across the actual-parent ancestry integration.
This test-only CI follow-up adds one existing retention test path,
bringing the final scope to 13 paths. Every unrelated master change,
report documentation fix, and migration 0397 is preserved. No database,
shared-worker, workflow or lockfile change belongs to this slice.

- **223 distinct selected cases pass:** 209 across five complete
transfer/publication/consent/runtime/app-wiring suites, plus 14 selected
real Board-producer and Teams-authority integration cases. The other
1,580 integration cases were skipped, not passed. The protocol and
production consumers use separate new PostgreSQL databases migrated
through the application migrator.
- A real 105-transfer regression failed against the original worker
because one registered startup pass selected 105 rows instead of
yielding at 100. The corrected worker uses the existing paged shared
contract. Restarting the same service midway through discovery selects
the first 100 again, restores all 105 saved deadlines and expires them
at the original deadline. SQL is quiet before the deadline and after
settlement, with no consent-card or upload requests. The one-shot
inventory still returns all 105 rows.
- A guarded mutation removing only cursor reset fails at five rows
instead of 100 after restart. The original production source was
restored before the complete suites. The initial regression fixture
incorrectly faked network timers before its first SQL connection and
timed out; that setup failure is retained separately from the corrected
105-versus-100 regression. No assertion or timeout was weakened.
- The real Board producer registers the worker before publication,
commits the transfer wake, preserves the saved expiry, remains SQL-quiet
until due and atomically projects expiry to a cancelled publication
without another provider request. Existing authority and consent
transitions remain covered.
- Complete lifecycle coverage retains all three consent phases,
precision rounding, rollback and held outer commit, exact-row hints,
expiry recheck, failed projection retry, corrupt-row fairness,
drain/resume, shutdown and separation from abandoned-attempt
maintenance. Production server typecheck, build and module boundaries
pass. Server production typechecking does not statically check test
files; those files were executed.
- [Current bounded reconstruction and failure
ledger](https://github.com/paperclipai/paperclip/blob/36f43922b0a408f76adb7de31280812e9b752019/doc/plans/2026-10-25-teams-consent-expiry-sleep-audit.md#bounded-reconstruction-and-current-parent-acceptance-october-28)
records exact scope, counts, guarded mutation, corrected timer setup,
earlier fixture failures and limitations. Earlier 222-case and
combined-stack results belong to their stated historical heads; they are
not added to 223 or claimed rerun here. The [stack
ledger](https://github.com/paperclipai/paperclip/blob/36f43922b0a408f76adb7de31280812e9b752019/doc/plans/2026-10-24-recurring-server-sleep-stack.md)
retains inherited failure history. No complete local repository-suite,
live Teams tenant, Cloud wake, host/database suspension or savings claim
is made.
- **Fresh exact-head Greptile 5/5 and all CI are required.** Actual
failing Ponytail findings must be repaired. A missing/stopped/paused
turn without a verdict is an authorized nonblocker; an active no-verdict
review waits at most 30 minutes from its original request/start
(25-minute monitor cutoff). Older scores and parent checks are not
accepted as current passes.

- **Preserved inherited CI failure:** prior head `36f43922` had
exact-head Greptile/Ponytail 5/5 but CI run `40708566195` server6 job
`122887395220` failed the retention fairness assertion (2,291 passed/one
failed/one skipped in that shard). A fixed final UUID reproduces the
wrong expectation that an undiscovered page-two row is already deleted.
The corrected test controls time, expects that row to remain before
discovery and requires its deletion in the next fair batch, then all
remaining history and idle SQL. The existing five-second wait budget and
global timeout are unchanged. All 13 retention lifecycle cases pass;
bypassing company rotation fails the corrected assertion, and the
original shared helper is restored byte-for-byte. Earlier 223-case Teams
acceptance remains valid for unchanged Teams source and is not relabeled
as a new complete repository run. [Full CI repair
ledger](https://github.com/paperclipai/paperclip/blob/9e84cfab1f795279c8cec413a946631f1d781bd2/doc/plans/2026-10-25-teams-consent-expiry-sleep-audit.md#inherited-retention-ci-fairness-repair-october-28).
Fresh CI and exact-head reviews are required for this test-only
follow-up; old scores are stale.

- **Current merge gates:** all 58 checks are now clean for this exact
head and Greptile is 5/5 with no actionable findings. The current
Ponytail turn is paused without a verdict, an authorized nonblocking
state. Workflow `40712464720` attempt 1 independently hit npm
`ECONNRESET` during the serialized8 job's tool self-installer, before
its tests ran. Exactly one targeted job retry was requested after the
original workflow finished; attempt 2 completed successfully on the
unchanged head. No code, dependencies, workflow, timeout or review timer
changed for that retry. The original setup failure is retained in its
[job
log](https://github.com/paperclipai/paperclip/actions/runs/40712464720/job/122901243926);
the successful replacement is [job
122910350074](https://github.com/paperclipai/paperclip/actions/runs/40712464720/job/122910350074).

## Risks

- Missed supported producer signals could delay expiry until restart.
Real insertion, cancellation, consent transitions, caller-owned
transactions and Board publication are covered.
- Corrupt or locked rows must retain retry without starving healthy
work. Paging, fairness, atomic rollback, redaction, drain/resume and
shutdown tests cover these paths.
- Independent SQL writers and other processes need their own
notification contract. No persistent DB listener is added.
- Attempt recovery still scans periodically until the next slice. A
local deadline is not a durable Cloud alarm. This PR does not claim
whole-instance sleep.
- This slice is reconciled with its landed parent; current-head remote
checks/review and live-master composition proof still gate merge. Later
stack duties remain separate PRs.

## Model Used

OpenAI GPT-6 through Codex, with repository inspection, tool use and
code execution. The exact serving model ID 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 linked existing issues or described the issue in-PR
following the relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [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-28 10:32:30 -05:00
..