Files
PaperClipAI/doc/idle-teams-attempt-recovery.md
T
DottaandPaperclip 7d75ae0569 fix(server): recover Teams attempts from saved claim deadlines (#17592)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Teams file delivery saves a claim before each provider operation.
> - A crashed operation can leave that claim unfinished.
> - Periodic maintenance scans for abandoned claims even when none
exist.
> - This PR wakes recovery from committed changes and the saved claim
deadline.
> - Empty work becomes quiet while unknown effects remain safe to
review.

## Linked Issues or Issue Description

**What existing behavior does this improve?**
Teams abandoned file-transfer recovery. This is slice 27, stacked on
consent expiry #17590.

**Current behavior**
The remaining Teams maintenance callback scans every 30 seconds for
expired transport claims. It runs from shared publication maintenance.

**Proposed behavior**
The Teams owner reconstructs existing claim deadlines at startup and
reloads a transfer after its committed update. At expiry it clears the
claim and preserves the existing unknown-outcome or conflict state.
Normal receipt settlement removes the deadline.

**Reason and benefit**
Remove the last periodic Teams maintenance scan without resending a
possibly delivered card or file.

**Breaking changes**
No schema or public API change. Explicit diagnostic recovery methods
remain compatible. Consent expiry stays with its separate parent worker.

## What Changed

- Add a Teams-owned worker for consent_sending, uploading,
file_info_sending and conflict rows with a saved attempt expiry.
- Notify from the existing versioned update before writes. Preserve
transaction ownership and shared uncertain-write reconciliation.
- Expose a narrow per-transfer recovery method that uses the existing
lock order, integrity checks and atomic projection.
- Remove the periodic Teams maintenance callback and its
publication-maintenance invocation.
- Add real claim, receipt, conflict, backlog, restart, precision,
failure, drain and shutdown tests. Wire the owner through existing app
admission gates.
- Retain the existing signoff cancelled-run selection test and GitHub
inline retry-window race assertion.
- Document supported writers, remaining sleep limits and the failure
ledger in `doc/idle-teams-attempt-recovery.md` and its linked audit.

## Verification

Published head: `24c54399909105984e2a10f5869064984f69c472`, targeting
master after consent #17590 merged at
`4a89456bb3f9aef87cfe0e922e8b9d1a777a6c7e`. A normal ancestry merge
reconciles the landed squash with the previously verified child. The
complete 13-path binary patch is identical before and after
reconciliation, and all 30 unrelated master paths are preserved. This
adds no new worker behavior; the 239-case local evidence below belongs
to the verified unchanged patch. All 58 current-head checks are now
clean, including the complete required CI run. Greptile is 5/5 at this
exact head. The current Ponytail turn is paused without a verdict, which
is an explicitly authorized nonblocker; no failing review is waived.

The complete 13-path slice preserves the reviewed attempt implementation
and inherits the parent's report migration 0397, bounded consent worker
and retention CI test repair. No new schema, queue table, shared
scheduler contract, workflow or lockfile change is added by this duty.

- **239 distinct selected cases pass:** 221 across five complete
transfer/publication/consent/runtime/app-wiring suites, 16 selected real
Board/Teams-authority/GitHub-retry integration cases, and two delegated
Slack policy cases. Other chat integration cases (1,579) and
connection-management cases (81) were skipped, not passed. Each database
owner uses a new isolated PostgreSQL database.
- Real Board publication saves an active attempt deadline; its confirmed
receipt retires recovery, and SQL stays quiet afterward. The inherited
consent producer and authority cases still pass. Lifecycle coverage
exercises claim stages, unknown-outcome quarantine without repeating
provider I/O, conflicts, rollback, restart, precision, backlog/fairness,
failure recovery, drain and shutdown. The changed GitHub metadata/retry
race passes without duplicate assessments.
- The old server10 CI failure, run `40557802358` job `122390238707`, was
a delegated Slack invitation rate-window assertion (2,122 passed/one
failed/15 skipped in that shard). The verified parent already contains
its Date-only fixture fix. Both linked and invitation cases now pass
their full caller/target/shared-policy scenarios on a fresh server
fixture; no duplicate policy change or new owned file was added. That
old failure is preserved rather than called a pass.
- Server production typecheck, build and module-boundary checks pass.
Test files were executed; production typechecking does not statically
check them. The existing test-only signoff cancelled-run selection fix
is retained but was not executed locally in this acceptance; fresh
remote E2E gates remain required.
- [Current-parent acceptance and preserved failure
ledger](https://github.com/paperclipai/paperclip/blob/24c54399909105984e2a10f5869064984f69c472/doc/plans/2026-10-25-teams-attempt-recovery-sleep-audit.md#current-consent-parent-preparation-and-acceptance-october-28)
records current scope, commands, counts and limits. Earlier 235-case and
combined-stack counts belong to their stated revisions and are not added
to 239. The [stack
ledger](https://github.com/paperclipai/paperclip/blob/24c54399909105984e2a10f5869064984f69c472/doc/plans/2026-10-24-recurring-server-sleep-stack.md)
retains inherited failure history. No complete local repository suite,
live Teams/Slack tenant, external wake, physical suspension or savings
claim is made.
- **Exact-head Greptile 5/5 and all 58 checks are clean.** Actual
failing Ponytail findings must be repaired; a missing/stopped/paused
turn without a verdict is an authorized nonblocker. An active no-verdict
turn waits at most 30 minutes from its original request/start (25-minute
monitor cutoff). Older reviews and parent checks are not treated as
current passes.

## Risks

- Recovery must not infer that an expired claim means a provider effect
did not happen. It retains unknown outcomes and does not resend.
- Late receipts must still match current claim identity. Current-state
rechecks and existing protocol tests preserve this fence.
- Supported deployment uses one active writer process through its owning
DB client. Independent SQL and additional processes need a separate wake
contract.
- Cloud external alarm coverage and sleep eligibility are unchanged.
Other chat duties still run periodically until their own migrations.
- Voice-report parent review repairs are propagated: explicit
single-active-writer scope and an indexed report window. Thirty
report-worker/startup tests pass on the combined child. Fresh remote
checks, older stack findings and final master reconciliation remain
required.

## 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:51:16 -05:00

4.6 KiB

Teams abandoned attempts and idle work

Teams transport-attempt recovery now runs from committed transfer changes and saved claim deadlines. The shared coordinator and scheduler own its lifecycle. Together with consent expiry, this replaces the periodic Teams maintenance callback. It adds no table, schema or independent timer.

Durable obligation

A transfer in consent_sending, uploading, file_info_sending, or conflict with attemptExpiresAt has an outstanding claim. The existing protocol writes an attempt ID and a 90-second deadline before provider I/O. A normal receipt clears both. A conflicting callback retains the original in-flight claim until its deadline; the conflict is not proof that the original operation stopped.

The protocol's central versioned update registers an awaited notification before the write, within the existing transaction. This includes normal claims, settlement, callbacks and caller-owned operator resolution. The DB work-signal boundary owns outer commit, rollback and uncertain transaction settlement. Inserts create unused consent, so they do not create an attempt obligation.

Startup reconstructs eligible attempts in 100-row UUID pages. A committed hint reloads its transfer ID. PostgreSQL timestamps round upward to a millisecond. An inspection does not move the saved deadline. A changed claim is reloaded before recovery; settled or deleted rows remove the cached obligation.

Recovery preserves uncertainty

At the deadline, the existing Teams owner locks publication then transfer, checks integrity, and rechecks the current phase and deadline. It clears the expired attempt and changes a sending phase to its corresponding unknown outcome. Conflict stays conflict with its original reason and private evidence. Publication projection and transfer mutation remain atomic. Lock waits remain bounded.

Recovery does not call a provider, resend a card or file, manufacture a receipt, or assert that an unknown effect never occurred. A delayed foreground receipt still has to match its current attempt ID and phase. Existing operator resolution and on-use authorization rules remain unchanged.

Unknown outcomes with no live attempt are passive review evidence. Unused consent is owned by the separate consent-expiry worker. Neither keeps attempt recovery querying. Failed loads, locks or projections retain bounded retry work; fair batches allow healthy rows and other companies to progress. Worker errors redact SQL parameters and private transfer envelopes.

Lifecycle and ownership

The Teams module selects phases and deadlines. The existing protocol performs recovery. Shared startup only registers the worker and admission gates. The shared scoped worker coalesces writes, preserves dirty scopes through drain, reconstructs after restart, and joins in-flight work during shutdown. An unresolved write may still need transaction-status reconciliation while dispatch is paused; this is not an empty maintenance scan.

The explicit combined expireAndRecover and attempts-only diagnostic methods remain available. Production processPublicationMaintenance no longer invokes Teams recovery. Other chat maintenance reasons are separate pending migrations.

Supported writes use this process's owning DB client. Another active server or independent SQL writer requires a separate notification contract. Cloud external alarm coverage and conservative sleep eligibility are unchanged. A registered local deadline cannot wake a stopped host. This migration proves worker quiet, not measured provider suspension or fleet savings.

Verification

Real PostgreSQL tests exercise all sending stages, conflict during a live send, normal settlement, interrupted receipt persistence, saved deadlines across restart, quiet history, drain/resume, rollback, precision, atomic projection failure, 102-entry paging/fairness and shutdown. The real Board producer integration registers the worker before sending, observes the saved claim while the provider receipt is held, and verifies that the committed receipt disarms recovery. Existing Teams authority, consent and publication cases remain covered. See the audit and failure ledger.

Current parent acceptance is recorded in the audit's current-parent section: 239 selected cases pass, with the old inherited rate-window CI failure and its already-landed fixture fix preserved. Actual landed-parent reconciliation and fresh remote checks remain merge requirements; historical counts are not current full-workspace verification.