mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-30 05:16:50 +01:00
fix(server): wake archive notification delivery from committed changes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
0956883a84
commit
692e3ee02c
9 files changed
+443
-96
No files matched your search
@@ -0,0 +1,86 @@
|
||||
# Decision-archive notification delivery
|
||||
|
||||
Auto-archive already writes `decision_archive_notification_outbox` in the same
|
||||
transaction that archives its retention row. The old retention sweep called
|
||||
notification delivery on every maintenance tick, including an empty outbox.
|
||||
Delivery is now its own coordinator worker. Retention scanning remains a separate
|
||||
migration; this PR does not change what is archived or when.
|
||||
|
||||
## Ownership and producer audit
|
||||
|
||||
`decision-retention.ts` owns eligibility, current-version checks, source authority,
|
||||
origin resolution, and archive writes. Its sole outbox producer is `autoArchive`.
|
||||
It now awaits the delivery notification immediately before inserting the outbox
|
||||
row in the existing transaction. Caller-owned transactions and savepoints retain
|
||||
outer commit/rollback ownership through the shared database signal mechanism.
|
||||
Manual archive/revive and reviewed-manifest archive do not create these
|
||||
notifications today; this change does not add notifications to those actions.
|
||||
|
||||
`decision-archive-delivery.ts` owns claim recovery, selection, grouping, delivery,
|
||||
and settlement. `decisionRetentionService.deliverNotifications` remains a
|
||||
compatible delegate. Its notification callback and batch type retain their
|
||||
existing contract. The scheduler only sees the delivery operation and next
|
||||
work deadline. Claim and acknowledgement writes belong to this worker; they do
|
||||
not broadcast self-generated wakes. There is no additional table or migration.
|
||||
|
||||
The server registers the worker in enabled-heartbeat startup housekeeping and
|
||||
removes notification delivery from `runRetentionSweep`. Registration scans saved
|
||||
work before the retention pass can enqueue more. The disabled-heartbeat branch
|
||||
continues not to dispatch agent notifications. A service without a callback
|
||||
leaves outbox rows intact and does not attempt notification delivery.
|
||||
|
||||
## Work and deadlines
|
||||
|
||||
Pending outbox rows are due for delivery. Each pass claims up to 500 rows and
|
||||
groups them by company and origin agent, preserving company scope and the
|
||||
existing notification payload. Failed or remaining pending rows retain a
|
||||
30-second retry. Never-attempted rows sort first, then the oldest attempts, so
|
||||
one failing batch cannot repeatedly hide later rows.
|
||||
|
||||
A delivering row retains its existing five-minute claim deadline. Startup
|
||||
reconstructs that deadline from `lastAttemptAt`; a legacy missing timestamp is
|
||||
immediately recoverable. A worker waiting only for a future claim deadline does
|
||||
not repeatedly query until that deadline. Settlement and failure reset require
|
||||
the same claim timestamp, so a delayed old result cannot settle or release a
|
||||
replacement claim. Delivered counts include only rows actually settled.
|
||||
|
||||
An empty outbox or retained delivered history has no worker deadline and no
|
||||
recurring SQL. The coordinator coalesces writes and prevents overlapping passes.
|
||||
The shared deadline worker preserves first enqueue during drain, changes during
|
||||
delivery, and failed retries across drain/resume. Coordinator shutdown joins
|
||||
in-flight work. These guarantees do not replace the durable rows.
|
||||
|
||||
## Recovery and limits
|
||||
|
||||
A failed enqueue rolls back both the archive mutation and its outbox insertion.
|
||||
An uncertain transaction retains the shared intent until its outcome is known.
|
||||
A notification failure returns the same rows to pending. A process stopping
|
||||
while delivering leaves the claim for deadline recovery on restart.
|
||||
|
||||
Delivery remains at least once. A wake accepted before a lost acknowledgement
|
||||
can be retried. This change does not promise exactly-once agent wakeups or add
|
||||
new deduplication storage. The five-minute stale threshold is unchanged.
|
||||
|
||||
Supported producers share the process-local database signal owner. Independent
|
||||
SQL or another active server needs a separate wake contract. The existing Cloud
|
||||
sleep proof still blocks this outbox conservatively; no external-wake coverage
|
||||
or sleep guard is relaxed. Worker quiet does not prove actual database or host
|
||||
suspension. Retention scanning and decision continuations remain separate duties.
|
||||
|
||||
## Verification
|
||||
|
||||
The final selection passes 54 tests: 15 real-PostgreSQL retention/delivery cases
|
||||
and 39 startup cases. Twelve new cases cover configured idle, retained delivered
|
||||
history, real enqueue, restart, exact stale-claim timing, legacy missing timestamps,
|
||||
first enqueue during drain, failed-delivery retry through drain, outer commit and
|
||||
rollback, enqueue failure rollback, writes during delivery, more than 500 rows,
|
||||
and stale-result fencing. Quiet assertions observe no reads or writes across an
|
||||
hour of controlled time. Workspace typecheck and build pass.
|
||||
|
||||
The first 52-case selection passed. Adding fault/concurrency tests then exposed
|
||||
two fixture errors: duplicate globally unique issue identifiers across two seeded
|
||||
companies, and asserting quiet before rollback reconciliation had run. Seeded
|
||||
identifiers now use their company prefix; quiet checks advance the retained retry
|
||||
and await authoritative settlement. All 54 cases passed on rerun. No production
|
||||
hold, recovery deadline, or timeout was weakened. Full repository CI and fresh
|
||||
review are required before this PR is ready.
|
||||
@@ -15,10 +15,10 @@ Do not mark an item done from a timer removal or a green check alone.
|
||||
| 01 | Custom-image setup expiry | Session writes, existing expiry, startup/retry/drain | [#17443](https://github.com/paperclipai/paperclip/pull/17443), all checks passed, Greptile 5/5 at `36844a458b` |
|
||||
| 02 | Remaining chat maintenance | Provider runtime discovery; voice reconciliation; milestones; wake notices; interaction continuations; terminal confirmations; GitHub recovery; Teams maintenance, separately by owner | Pending |
|
||||
| 03 | Run and task recovery | Orphan runs and active leases; shutdown workspace settlement; scheduled retries; queued runs; stranded assignments; resolved dependencies; task watchdogs; silent-run monitoring; stale locks | Pending |
|
||||
| 04 | Decision retention and notifications | Retention deadlines and notification delivery are separate reasons | Pending |
|
||||
| 04 | Decision retention and notifications | Retention deadlines and notification delivery are separate reasons | Retention pending; notification delivery implemented in slice 08, PR pending |
|
||||
| 05 | Decision expiry and interrupted effects | Expiry, effect recovery, continuation recovery | Pending |
|
||||
| 06 | Tool connection health | Configuration/use activation and defined proactive health deadlines | Pending |
|
||||
| 07 | Secret proposals, tool reviews, gateway tokens | Separate expiry/scrub/recovery owners | Secret proposals: [#17446](https://github.com/paperclipai/paperclip/pull/17446), checks passed at `d41590ac0e`; gateway credentials: [#17450](https://github.com/paperclipai/paperclip/pull/17450), all checks passed, Greptile 5/5 at `c5c53fc43f`; pending tool-review expiry: [#17453](https://github.com/paperclipai/paperclip/pull/17453), all checks passed, Greptile 5/5 at `ce6a260a83`; approved recovery: [#17454](https://github.com/paperclipai/paperclip/pull/17454), all checks passed, Greptile 5/5 at `7a1cf03e62`; interrupted claims: [#17458](https://github.com/paperclipai/paperclip/pull/17458), all checks passed, Greptile 5/5 at `85003a7ac4`; projection implemented, PR pending |
|
||||
| 07 | Secret proposals, tool reviews, gateway tokens | Separate expiry/scrub/recovery owners | Secret proposals: [#17446](https://github.com/paperclipai/paperclip/pull/17446), checks passed at `d41590ac0e`; gateway credentials: [#17450](https://github.com/paperclipai/paperclip/pull/17450), all checks passed, Greptile 5/5 at `c5c53fc43f`; pending tool-review expiry: [#17453](https://github.com/paperclipai/paperclip/pull/17453), all checks passed, Greptile 5/5 at `ce6a260a83`; approved recovery: [#17454](https://github.com/paperclipai/paperclip/pull/17454), all checks passed, Greptile 5/5 at `7a1cf03e62`; interrupted claims: [#17458](https://github.com/paperclipai/paperclip/pull/17458), all checks passed, Greptile 5/5 at `85003a7ac4`; projection: [#17468](https://github.com/paperclipai/paperclip/pull/17468), Greptile 5/5 at `0956883a84`, CI running |
|
||||
| 08 | GitHub connection events and continuity | Event discovery/replay and credential continuity separately | Pending |
|
||||
| 09 | Merged-PR confirmations | Candidate writes, webhook activation where supported, outstanding-candidate fallback | Pending |
|
||||
| 10 | External-object refresh | Configuration changes, refresh deadlines, source events | Pending |
|
||||
@@ -284,3 +284,24 @@ and build pass. One initial interaction cleanup-hook timeout is recorded in
|
||||
the behavior document; the complete isolated suite then passed. Both parent
|
||||
PRs #17454 and #17458 now have every check passed and are mergeable, including
|
||||
the additional Paperclip Review checks. Full CI/review for slice 07 is next.
|
||||
|
||||
|
||||
## Slice 08: decision-archive notification delivery
|
||||
|
||||
Projection repair is #17468, head `0956883a84`, with Greptile 5/5 and no open
|
||||
findings. Latest checks are running with no failure. Slice 08 is based on that
|
||||
branch and separates archive notification delivery from retention scanning.
|
||||
The existing auto-archive transaction is the sole outbox producer. A committed
|
||||
enqueue wakes delivery; pending rows retain retries and delivering rows retain
|
||||
their saved five-minute claim deadline. Delivered history is quiet.
|
||||
|
||||
The implementation preserves the delivery API and payload. It fences stale
|
||||
completion by claim timestamp and orders unattempted/oldest-attempted pending
|
||||
rows so failed batches do not starve later work. No table or migration is added.
|
||||
See [archive delivery](../idle-decision-archive-delivery.md).
|
||||
|
||||
All 54 selected tests pass, including 12 new real-PostgreSQL lifecycle cases.
|
||||
Workspace typecheck/build pass. Two added-fixture failures and their corrections
|
||||
are recorded in the behavior document. Full CI/review remains required.
|
||||
Retention eligibility/scanning, decision expiry, effect recovery, and decision
|
||||
continuation delivery remain independent unfinished reasons in the full ledger.
|
||||
Reference in new issue
Block a user