Commit Graph
3 Commits
Author SHA1 Message Date
DottaandPaperclip 06c0566a08 fix: make task Retry work after a refused automatic attempt (#19466)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task Retry stores a durable request before it starts a new run.
> - An automatic attempt can stop at the execution gate before a
provider starts.
> - That cancelled attempt becomes the source of the next Retry click.
> - Admission rejected that source, or chose an older run that did not
match the stored Retry authority.
> - This pull request verifies the refused attempt and keeps it as the
exact authority source.
> - The user can start one new turn after the older execution holds pass
their existing checks.

## Linked Issues or Issue Description

**What happened?**

Retry can remain deferred when the latest run was cancelled with
`execution_reconciliation_required` before dispatch. An older failed
preparation has an execution recovery hold. The Retry receipt names the
cancelled child, but admission only accepts a failed preparation as an
extra source. It can also choose the older run as `previousRunId`, which
fails the exact source check at dispatch.

**Expected behavior**

Retry starts one new turn when the latest attempt has a verified
pre-dispatch refusal and every older execution hold is ready. Provider
activity, retained execution resources, missing receipts, or a live
earlier process keep the hold.

**Steps to reproduce**

1. Seed a failed native preparation and its recovery hold.
2. Let a later automatic attempt be cancelled by the stale queued
execution gate before dispatch.
3. Click Retry against that latest cancelled attempt.
4. Before this change, continuation admission returns no receipt. After
this change, one successor starts with authority bound to the clicked
attempt.

**Paperclip version or commit**

Reproduced on `2f35eb1080`. Rebased onto `6cafcaf89b` for submission.

**Deployment mode**

Local source checkout with embedded PostgreSQL and the native Runner on
macOS arm64.

Refs #15383 and #16939. Those changes cover legacy conversation
cancellation and queue promotion. This change covers the exact source of
a task Retry receipt after a gate refused its automatic child.

## What Changed

- Reuse the existing database-backed pre-dispatch refusal proof when
admitting the exact Retry source.
- Keep the clicked source as `previousRunId`. Continue to check every
older execution hold.
- Add database regressions for safe refusal, unsafe evidence, concurrent
dispatch, and duplicate clicks.
- Add an opt-in acceptance test that drives the authenticated Retry
route through a real Codex turn and task finalization.
- Document the admission contract.

## Verification

- TDD: the new database regression returned `null` on the original code.
It passes with the repair.
- Final head `c43a84b1bf`: 487 tests pass across task Retry, explicit
native continuation, and execution continuation. Server TypeScript and
module boundary checks pass.
- Live acceptance: `PAPERCLIP_LIVE_REFUSED_RETRY=true
PAPERCLIP_RUNNER_BINARY=<current-source-runnerd> pnpm --dir server exec
vitest run --config vitest.config.ts
src/services/native-runtime/refused-task-retry.live.test.ts`.
- The real HTTP request returned 202 on the final rebased head
`c43a84b1bf`. Codex `gpt-6.1-sol` with high effort produced the
requested file. The task became done, both task locks cleared, and
finalization committed. A repeated click reused the same receipt and did
not create another run. The source file stayed unchanged.
- Server TypeScript check and module boundary check.
- `pnpm verify --explain` returned 2 because the execution contract doc
requires broad CI coverage. The required CI checks supply that coverage.

## Risks

This changes execution admission. The existing proof requires the stored
refusal receipt and no provider work or retained execution resources.
Earlier recovery holds, process stop checks, decisions, actor authority,
and task ownership still apply. No schema change or migration.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use, and code
execution. The host does not expose an exact author model ID or context
window. The live acceptance provider was `gpt-6.1-sol` with high
reasoning effort.

## 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 — all GitHub Actions checks pass
on `c43a84b1bf`; Paperclip Review completed at 5/5 with no findings
(neutral check conclusion).
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
reviewed `c43a84b1bf` with no actionable findings
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-06 07:03:54 -08:00
4bdcd1cffc fix(recovery): release holds for unstarted dispatch cancellations (#18838)
Release false recovery holds for legacy dispatch cancellations whose retained
evidence proves execution never started. Keep audit history, current admission
gates, review-wait Retry, and retained workspace obligations.

Share the execution-absence proof across cancellation policies and make the
dispatcher error-code policy exhaustive. Add PostgreSQL coverage and stabilize
the browser history fixture by waiting for pause cleanup before seeding it.

Fixes #15483
Refs #18085

Co-authored-by: commitperclip[bot] <284812584+commitperclip[bot]@users.noreply.github.com>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 14:54:59 -06:00
DottaandPaperclip 8a3d2cb04f fix(recovery): avoid recursive holds for undispatched waits (#16894)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Queue admission must stop work while a previous execution needs
recovery.
> - The final dispatch gate can cancel a queued run before it starts.
> - Legacy recovery treats this known wait as an unknown provider
outcome and creates another hold.
> - The extra hold can survive repair of the original execution and
leave saved messages stuck.
> - This pull request uses one database proof to prevent the extra
incident and recognize old false holds.
> - Saved input can then pass through normal admission with the original
work and safety gates intact.

## Linked Issues or Issue Description

Refs #15483.

**What happened?**

The dispatcher writes an `execution_reconciliation_required`
cancellation when an existing recovery hold denies a queued run. No
runtime is selected and no provider starts. Legacy recovery later
creates a `legacy_execution_requires_reconciliation` action for that
cancelled admission. Its settled `replay: blocked` record remains an
effective hold after the real source recovery finishes. The task has
saved messages but no eligible Retry or Continue action.

**Expected behavior**

A verified admission denial stays a wait. It must not create another
provider-outcome incident. Existing false incidents must stop blocking
after the server verifies their recorded source. Real execution,
uncertain outcomes and file recovery stay held. The harness determines
these infrastructure facts; the user does not certify them in a form.

**Steps to reproduce**

1. Persist a queued-run gate cancellation with its task-bound
`executionWait` receipt and no execution authority or resource evidence.
2. Run legacy reconciliation. On the old code, it creates a second
incident.
3. Persist the old incident as resolved with replay blocked, then clear
the original source hold.
4. Restart queue recovery with saved user messages. On the old code, no
successor is admitted.

Related work: #15965 covers other pre-start dispatch cancellations but
explicitly excludes this existing-hold code and leaves historical holds
unchanged. #16634 covers stale-cancel classification and queue drain,
without re-evaluating historical effective holds. #15383 covers an
operator Stop during conversation startup. This change addresses the
existing-hold receipt and its persisted false incident; it does not copy
those broader changes.

## What Changed

- Add one database proof for a legacy admission denied by an existing
execution hold. Require the exact dispatcher receipt, matching task
identity, no runtime or process claim, no lease or finalization
coordinator, and no execution events.
- Use the proof before legacy recovery creates an incident and when it
computes effective historical holds. Keep the old audit rows.
- Preserve source file obligations and the original recovery action.
Keep the normal queue, pause, ownership, company and budget checks.
- Add real PostgreSQL regressions for incident prevention, old state,
concurrent queue recovery, message order, conflicting evidence and scope
boundaries.
- Update the execution contract and both recovery maps.

## Verification

- Red: the regression on unfixed source fails on incident
classification, historical hold projection and actual periodic
saved-message admission. The other 24 safety cases pass.
- Green: all 27 new database cases pass. Two safety assertions were
aligned with the existing contract: daily limits stop dispatch after
queue promotion; a human comment can intentionally reopen a terminal
task. The final regression checks the real daily dispatch gate and agent
pause.
- Existing `explicit-native-continuation.test.ts`: 255 tests pass.
Existing legacy recovery and PostgreSQL dispatcher suites: 61 tests
pass.
- `pnpm -r typecheck`, `pnpm build`, `node scripts/check-maps.mjs` and
`git diff --check` pass.
- GitHub PR CI run `39313204660` passed on
`ef6009e656b7c9b4c788480dce322874565960ac`. All 54 successful check-runs
and the Snyk status are green. Two optional Storybook checks were
skipped. The independent Paperclip Review completed with a neutral check
and a 5/5 assessment with no findings. Greptile also reports 5/5 on the
same commit. There are no unresolved review threads.
- `pnpm test:run` is still running locally. Its overall result is
pending; no full local suite pass is claimed. The exact-head GitHub CI
suite passed.
- A read-only probe of an affected hosted task verifies the same
database proof and projects no false blocker with the working source. No
live rows were changed. Deployment and a real resumed provider run
remain unverified.

## Risks

- An incorrect exclusion could admit work with uncertain effects. The
proof fails closed on missing receipts, source mismatches,
runtime/process ownership, any retained lease, coordinator, execution
event or file obligation. Real upstream file recovery remains blocking
in the regression.
- The effective-hold query adds indexed existence checks scoped to the
recorded run and company. It makes no provider calls and performs no
database repair writes.
- Historical audit records keep their recorded outcome. Only their
effective blocking status changes when the server can prove the
admission never ran.
- No schema, permission, credential or UI form changes are included.
Live recovery still needs verification after rollout.

## Model Used

OpenAI Codex, model ID `gpt-6.1-sol`, with reasoning, tool use and code
execution.

## 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>
2026-10-19 15:26:39 -05:00