Files
PaperClipAI/maps/agent-access-map/tasks.md
T
DottaandPaperclip 98d17da1e2 Fix native monitor dispatch and finalization recovery (#16975)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native runs persist their results and workspace before they release
execution.
> - A task monitor must wait for that settlement before it starts the
next run.
> - The scheduler checked provider status but did not check unfinished
finalization.
> - A timer could dispatch too early, then its old finalizer could no
longer find the consumed schedule.
> - This pull request delays dispatch and reconciles prior delivery from
server records.
> - The task can continue without repeating the provider turn or
removing a newer run's ownership.

## Linked Issues or Issue Description

**What happened?**

A native run saved a yielded monitor result while workspace finalization
needed recovery. Its timer dispatched after the provider stopped. The
follow-up failed because the earlier run still held workspace recovery.
The old finalizer then retried `monitor_wait_authority_lost`
indefinitely.

**Expected behavior**

Timer dispatch waits for native finalization. If an older timer already
dispatched, the finalizer uses its saved scheduling receipt and admitted
successor to settle the old result. It preserves current task state and
ownership.

**Steps to reproduce**

1. Schedule a task monitor from a native run and save a yielded result.
2. Leave native finalization retryable after the provider reaches a
terminal state.
3. Let the monitor become due. Before this change, the scheduler
consumes it.
4. Restore workspace persistence and retry the old finalizer. Before
this change, the missing schedule leaves it retryable.

**Paperclip version or commit**

Reproduced against master `34be1b831bcaba070264ae8d214ceffe6a8f9eab`.

**Deployment mode**

Self-hosted server built from source, with PostgreSQL and the native
runner. Related recent recovery work: #16962 and #16963. No duplicate
repair found for this failure.

## What Changed

- Scheduled and manual monitor dispatch wait for unfinished native
finalization. If the monitor expires during workspace recovery, the
accepted result can settle into the persisted schedule, and the
scheduler then applies its existing expiry policy. New monitor-wait
results still require an unexpired, unexhausted schedule.
- A narrow reader joins the run's server-issued monitor receipt to its
durable wake and admitted successor.
- The finalizer and locked status commit verify that evidence and
preserve current task ownership. They do not create another wake.
- Monitor delivery participates in assessment identity, so recovery can
replace a prior error decision without violating the decision ledger's
uniqueness constraints.
- PostgreSQL regressions cover saved-result replay, active successors,
wrong task or agent, missing receipts, unadmitted wakes, ownership
races, and failed workspace persistence.
- The execution contract and verification maps describe the recovery
boundary.

## Verification

- Red: the saved monitor regression remained `retryable_failure` instead
of `committed` on unpatched code.
- Red: the scheduler admitted a wake while terminal-run finalization
remained retryable.
- Red/green: two PostgreSQL cases reproduce timeout during workspace
recovery for scheduled and manual checks. Before the review repair, both
remain `retryable_failure` with `monitor_wait_authority_lost`.
Afterward, both commit, apply board escalation once, preserve the saved
result, and enqueue zero provider wakes. New completion still rejects
the expired schedule.
- Typecheck: `pnpm -r typecheck` passed.
- Build: `pnpm build` passed.
- Maps: `node scripts/check-maps.mjs` passed.
- Review: Greptile 5/5 and Ponytail Paperclip Review 5/5 on the current
head, with no unresolved findings. Both reviews are static; execution
evidence is recorded separately above and below.
- Linux staging: the original repair passed 170 tests across monitor
integration, scheduling, status arbitration, and workspace finalization.
The expanded suite passes all 172 tests, including both new timeout
regressions.
- Live acceptance: deployed the original repair to the authorized Linux
ARM64 self-hosted instance. The stranded saved result finalized
automatically; the original task then completed through normal
admission, with its final answer persisted after browser reload. The
other original task was already complete and its external delivery was
verified without repeating it.
- The initial Mac regression run passed 68 tests, but three suites
failed during embedded PostgreSQL startup. A subsequent focused run
confirmed the host had exhausted its PostgreSQL semaphore allowance.
Linux verification uses isolated databases; the local failure logs
remain preserved.
- The local full test suite is not claimed as passed. It is blocked by
the same PostgreSQL startup limitation; full-suite CI passed on head
`8bc29d1b7783446f397b858e558083570a96bb66`
([run](https://github.com/paperclipai/paperclip/actions/runs/39447913466)).

## Risks

- A delivery match must not grant new execution authority. The lookup
requires the original run's server receipt and a matching company,
agent, task, wake, and admitted successor. The locked commit rejects
old-run checkout ownership and permits no status effects.
- Workspace persistence must succeed before settlement. Its existing
failure path remains effective.
- The scheduler waits while a native finalizer is unfinished. Workspace
recovery must settle before dispatch; elapsed monitor limits transfer to
the existing scheduler recovery policy instead of stranding the result.
Neither settlement path renews a deadline or changes budget, approval,
pause, or provider authority.
- No schema, migration, credential, or provider configuration change.

## Model Used

OpenAI Codex, based on GPT-6, with tool use and code execution. The
exact backend 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 (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-20 10:58:41 -05:00

10 KiB

Task creation and lifecycle — agent access

Product map: Task creation and lifecycle.

Review date: 2026-10-12. Source commit: 58e16dbddfc9c8d0cf60867f9d9847f0feda055b. Native REST transport reconciled in this change; see the shared transport contract.

This is a source-reviewed baseline of representative operations and known gaps. Each capability needs operation-level review before its entire scope can be qualified. No live agent execution is claimed. Follow the map contract for format, permission checks, evidence and maintenance.

create

Review: baseline.

API: present — representative operation IDs:

  • POST /api/companies/{companyId}/issues.

Agent access: available — Native create_task; REST / CLI / MCP.

Policy: Company/task visibility, assignment/change policy, active-run checkout ownership and lifecycle rules. Ordinary Board comments preserve queued/running retries; only a parked retry can be superseded. Explicit Stop/Interrupt retain their existing actor and execution authority. Agent comment tools do not acquire Board identity or cancellation authority through this behavior.

Evidence: source-only — implementation at the reviewed commit above.

Verification: not-run — exercise a listed permitted operation with actual agent credentials/run context; inspect its result and applicable persistence/attribution. Assert the relevant denied actor, owner, company or mode from Policy. Review the remaining operations before qualifying the whole capability.

Gaps: Creation is implemented; agent/user assignment and attachments may need narrower/different APIs.

organize

GET /api/companies/{companyId}/issues/summaries?ids=... reads up to five exact UUIDs and returns only navigation summaries. Normal company and task-read permissions apply to ordinary agent REST calls; task-bridge keys are denied. Hidden, missing, foreign and private inaccessible IDs are omitted without an existence disclosure. No dedicated CLI/native tool is added. Embedded database route tests cover same-company agent access and company/task-bridge denials; this does not establish a live native call_api journey.

The issue-list API accepts optional includeAgentCreatedTasks; false restricts independent results to human creation before search/pagination. Omission retains existing agent/API behavior. includeParticipatedTasks=true requires a board user and retains only that authenticated user's existing participation as a creation visibility exception. Company/private-task access checks still apply. Collection preferences and hierarchy presentation are browser state, not additional authority. Source-reviewed behavior; live agent access verification remains separate.

All chat_channel origins count as bot-created for creation visibility, including human-initiated conversations. Existing inbox participation remains independent.

Review: baseline.

API: present — representative operation IDs:

  • GET /api/companies/{companyId}/issues.

Agent access: available — Native search_tasks; REST / CLI; UI grouping/navigation state separately.

Policy: Company/task visibility, assignment/change policy, active-run checkout ownership and lifecycle rules.

Evidence: source-only — implementation at the reviewed commit above.

Verification: not-run — exercise a listed permitted operation with actual agent credentials/run context; inspect its result and applicable persistence/attribution. Assert the relevant denied actor, owner, company or mode from Policy. Review the remaining operations before qualifying the whole capability.

Gaps: Search/filter API exists; sorting/grouping/presentation defaults are not necessarily shared server preferences.

properties

Review: baseline.

Agent REST callers can promote a prepared backlog task with PATCH /api/issues/{id} and exactly { "status": "todo", "expectedStatusVersion": N }. The optional nonnegative integer version comes from the current task response. The server checks both the version and backlog state under the task row lock; status or ownership changes, including changes back to their earlier values, return 409 without promoting or waking the task. Mixed mutation fields return 422. Existing company, task, assignment and run authority still apply. Route and real-PostgreSQL service tests cover the contract; no live agent journey or dedicated native tool is claimed.

API: present — representative operation IDs:

  • PATCH /api/issues/{id}.
  • PUT /api/issues/{id}/title.

Agent access: available — Native set_task_title/reassign_task/set_dependencies and set_task_monitor; call_api for REST-authorized fields; agent REST. Monitor scheduling is a dedicated native tool, not a generic call_api operation.

Policy: Company/task visibility, assignment/change policy, active-run checkout ownership and lifecycle rules. A legacy run can assign its own task to a human without cancelling itself only with its signed run token and matching active run, agent, company and task. The outgoing run cannot mutate its human-owned task afterward, including through a late update already admitted before the handoff. Agent keys, native runs, board reassignment and transfers to another agent retain verified-stop requirements.

Native monitors require standard work mode and an agent-owned task in progress or review. The responsible user's current permissions also apply. A malformed request is rejected before mutation; bounded schema guidance lets the provider correct it without exposing submitted values. Receipt replay never re-arms a consumed schedule.

Evidence: source-only — implementation at the reviewed commit above.

Verification: not-run — exercise a listed permitted operation with actual agent credentials/run context; inspect its result and applicable persistence/attribution. Assert the relevant denied actor, owner, company or mode from Policy. Review the remaining operations before qualifying the whole capability. Focused route regressions in ownership tests cover a human handoff and stop enforcement for other actors, runtimes and targets; they use mocked services and do not claim live execution proof.

Queue and HTTP/2 sandbox callbacks admit GET /api/issues/{id}/diagnostics/wakes and POST /api/issues/{id}/monitor/check-now. The controller still checks task read access or monitor-management permission and assignment ownership. Recovery resolution and scheduled-retry controls remain outside this bridge surface.

Monitor verification: submit flat scheduling fields, observe the schema rejection and unchanged task, then retry with the nested monitor object and the same idempotency key. Inspect the saved schedule and audit, yield, and verify exactly one issue_monitor_due wake. Scheduler integration coverage uses real PostgreSQL, the native tool authority, and the heartbeat scheduler with a no-op process agent. It does not prove live model recovery.

Monitor deferral diagnostics: an unresolved execution-recovery hold keeps a due schedule intact and prevents execution. Read issue.monitor_deferred activity for the safe blocker cause; unexpected dispatch failures record issue.monitor_dispatch_failed. Repeated identical diagnostics are throttled for five minutes. The scheduler regression proves that a held source does not replay and that clearing the hold permits one later wake. This is local PostgreSQL evidence, not live provider recovery. Native timer dispatch also waits for unfinished finalization after a terminal provider run. Recovery may use that run's scheduling receipt and the matching admitted successor as historical delivery evidence. It does not grant new execution authority or clear a successor's ownership. Missing receipts, unadmitted wakes, and unfinished workspace persistence remain held. An accepted result may settle into a schedule that expired during recovery; the scheduler applies its existing expiry policy afterward. This does not authorize a new provider result to wait on an expired or exhausted monitor.

Gaps: Dedicated tools differ in scope and fields. Generic call_api forwards status, assignment and dependency fields to existing route authorization.

discussion

Review: baseline.

API: present — representative operation IDs:

  • POST /api/issues/{id}/comments.
  • POST /api/companies/{companyId}/issues/{issueId}/attachments.

Agent access: available — Native report_progress/comment_on_task/register_deliverable; REST / CLI / MCP.

Policy: Company/task visibility, assignment/change policy, active-run checkout ownership and lifecycle rules.

Evidence: source-only — implementation at the reviewed commit above.

Verification: not-run — exercise a listed permitted operation with actual agent credentials/run context; inspect its result and applicable persistence/attribution. Assert the relevant denied actor, owner, company or mode from Policy. Review the remaining operations before qualifying the whole capability.

Gaps: Other-task comments and active-task progress have separate scopes. Generic call_api forwards explicit reopen/resume/interrupt fields to the existing route checks; it cannot impersonate a user.

completion

Review: baseline.

API: present — representative operation IDs:

  • PATCH /api/issues/{id}.

Agent access: available — Native paperclip_finish/paperclip_block through runner protocol; agent REST / native call_api disposition under route permissions.

Policy: Company/task visibility, assignment/change policy, active-run checkout ownership and lifecycle rules.

Evidence: source-only — implementation at the reviewed commit above.

Verification: not-run — exercise a listed permitted operation with actual agent credentials/run context; inspect its result and applicable persistence/attribution. Assert the relevant denied actor, owner, company or mode from Policy. Review the remaining operations before qualifying the whole capability.

Gaps: The server governs whether a reported result becomes done/review/blocked. Generic lifecycle PATCH uses existing REST authorization, checkout and approval rules.