Files
PaperClipAI/doc/plans
8ac5a128e6 fix(server): resolve native finalization notices from committed wakes (#17758)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native finalization can commit before its recovery notice is
resolved.
> - The periodic orphan pass revisits committed runs to resolve those
notices.
> - Retained completed history should not require repeated discovery.
> - This PR moves that one duty to transaction wakes and concrete
failure retries.
> - Existing notice rows reconstruct work after restart. The action
service still owns resolution.
> - Other completion and resource duties keep their current recovery
paths.

## Linked Issues or Issue Description

Refs #17752 (stack parent: committed run projection), #17748
(accepted-result commit), and #17734 (workspace finalization). These are
distinct recovery obligations. This slice incorporates parent #17752 at
`a49a2dac13b0e1c19da5d3d6b435c781703b1c0b`, now merged as
`50fe32684d2d4e1db129e3c1bb455571344a0c09`. Related #17724 retires
obsolete exact-session notices for terminal runs with no result and
preserves their replay hold. This slice resolves notices after a result
committed; it does not change that no-replay policy.

**Current behavior**
Committed native finalization recovery repeatedly inspects active
watchdog notices. A notice can also be updated in place to describe a
different run, so resolving a previously selected ID alone is unsafe.

**Proposed behavior**
Wake a scoped worker from relevant committed writes. Reconstruct active
finalization notices at startup. Resolve only the current notice after
the native owner rechecks its binding under lifecycle locks. Stop
scheduled work when no matching obligation remains.

**Reason and benefit**
Notice recovery becomes quiet after settlement and reacts to real
changes without a polling delay. Reused notices remain attached to their
current incident. This does not claim that every remaining server duty
or the host can sleep yet.

## What Changed

- Track the initial recovery-notice sweep through shutdown without
blocking HTTP readiness on task locks.
- Require issue/run scope for ordinary finalization fanout. Routine
company-only hints leave dormant notices quiet; dedicated owner and
explicit settings signals retain their scopes.

- Extract the native finalization notice predicate and resolution owner.
Keep the existing recovery-action service responsible for writing the
resolution.
- Bind candidates by company, issue and run. Require a committed result
and an active or escalated finalization notice. Exclude held ownership,
pending status effects and unfinished run projection.
- Recheck coordinator, issue, run and matching actions under locks. A
newer incident cannot be resolved from a stale candidate snapshot.
- Use the shared scoped worker, transaction signals, retry backoff and
lifecycle handling. Add no table, per-feature timer or provider branch.
- Signal action insertion, update, supersession and resolution;
finalization and effect changes; run projection and ownership release;
issue changes; settings activation; and the recovery route that reopens
an action.
- Preserve no-op resolution quiet. Serialize only the three supported
notification IDs, even when a caller supplies a larger internal object.
- Delegate only notice resolution from the periodic path. Keep
manual/default finalization and other completion/resource work.
- Add real PostgreSQL producer, concurrency and lifecycle coverage and a
detailed audit.

## Verification

- Current local source `730e00a7b007a36d188ca0a328ef050d3a25d1ee`
incorporates the parent with a normal merge. All 1,209 parent-only,
eight owned-only and 12 automatic shared paths are preserved. The two
manual paths retain both chat import sets and scope the new notice
fanout. The entire package tree matches parent profile 80 byte-for-byte.
- Controlled real-database regression: original notice-only unscoped
fanout produces three candidate SELECTs for routine company hints. The
same zero-query assertion passes after restoring scoped fanout. The real
notice writer then creates and resolves an action for a committed run
and returns to quiet. Both phases restore production bytes exactly; the
22 cases outside this focused pattern are not counted as passing there.
- The complete main command passes **1,873 cases in 21 files**, without
skips, in 309.69 seconds. It includes all 23 notice-worker cases, full
presentation/ownership read coverage, 88 startup cases, and the
projection/result/physical-work consumers. Process recovery passes 410
in 163.54 seconds and external-chat response passes 273 in 53.48
seconds. Total complete-file acceptance: **2,556 cases in 23 complete
files**, with no skips. The additional focused chat selection passes 18
cases in 13.44 seconds; 1,796 unselected chat cases do not count. Total
selected passes: **2,574**. Normal merge
`a0f2b3f4348b078219a53d8cc135c84202ba505e` adopts actual parent ancestry
with this complete tested child tree unchanged.
- The chat selection covers the real reply/notice row-lock regression,
Slack and Telegram response recovery, current-authority denial cases,
exact canonical response binding and internal GitHub attachment
navigation. Other cases in the giant chat file remain unselected; this
is not full chat-suite acceptance.
- Production server typecheck, build, module boundaries, migration order
and source identity checks pass. Twenty dedicated PostgreSQL 17
databases use the unchanged migration 413 schema. Embedded-only
invocations use the existing fresh-per-call recovery helper. All
temporary imports restore exactly and all 24 selected test hashes match
after completion. No assertion or timeout is relaxed.
- Parent package acceptance remains applicable: 130 passing
installation/compatibility/transport cases, 22 installed ACPX/Grok
contract cases and 26 native-provider Rust cases. One existing opt-in
closed Pi snapshot case is skipped because its distribution root is
unset. This does not qualify a live provider.
- Failure history and scope are retained in
`doc/plans/2026-10-26-native-recovery-notice-sleep-audit.md`.
Current-head acceptance: all 57 checks are clean, including protected CI
and security. Greptile gives 5/5 on
`2a8777bbed8b1e10810e3d6d99aae3f25b4854ef`, with no actionable findings.
Prior scores do not substitute. No Ponytail verdict is present under the
authorized missing-review policy. No full local repository suite,
deployed sleep, expanded Cloud wake coverage or measured savings is
claimed.

## Risks

- Resolution takes coordinator, issue, run and action locks in the
native recovery order. Contention must preserve the current incident;
the real concurrent-upsert and competing-resolver cases exercise this
boundary.
- Process-local signals cover instrumented application writers. They do
not add support for independent SQL writers or replicas without
supported ingress. Startup reconstructs the existing durable rows.
- Completed finalization is not proof that external conversation
settlement, Muse reconciliation or physical cleanup finished. Those
obligations retain their owners and recovery paths.
- Discovery uses narrow keyset pages. No fleet benchmark, host
suspension or cost reduction is claimed. Cloud wake coverage remains
unchanged.
- Parent #17752 has merged. This PR targets master at
`50fe32684d2d4e1db129e3c1bb455571344a0c09`. Fresh current-head CI and
review must pass before the authorized protected merge.

## Model Used

OpenAI Codex, GPT-6 family, with repository tools and code execution.
The exact serving model ID and context-window size are not exposed in
this session. No subagent was used for this slice.

## 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
- [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>
Co-authored-by: Codex <noreply@openai.com>
2026-10-29 05:35:01 -05:00
..