Skip to content

fix(server): restamp task watchdog mutation scope after the run's own writes (SPA-2178 / SPA-353) - #11111

Open
JamesSparkMojo wants to merge 2 commits into
paperclipai:masterfrom
JamesSparkMojo:dex/spa-2174-actor-aware-restamp
Open

fix(server): restamp task watchdog mutation scope after the run's own writes (SPA-2178 / SPA-353)#11111
JamesSparkMojo wants to merge 2 commits into
paperclipai:masterfrom
JamesSparkMojo:dex/spa-2174-actor-aware-restamp

Conversation

@JamesSparkMojo

@JamesSparkMojo JamesSparkMojo commented Aug 9, 2026

Copy link
Copy Markdown

Thinking Path

  • Paperclip is the open source app people use to manage AI agents for work.
  • A task watchdog wakes on a stopped issue subtree and may run sanctioned recovery mutations.
  • Each run pins a stop fingerprint at wake. revalidateMutationScope re-derives the fingerprint before every gated mutation and rejects the mutation when the fingerprint has changed.
  • But the fingerprint derives from the subtree leaves. The watchdog's own first material write moves those leaves. Every later gated mutation in the same run then returns 409 currentState: live or 409 stop fingerprint changed. The 409 says refresh the source state, but no refresh path exists.
  • The fix must let one run make several sanctioned writes to distinct stopped leaves. It must still block the run from acting on a leaf a third party changed after wake.
  • This pull request adds restampCurrentRunAfterOwnWrites(scope, classification, observedFingerprint, ...) to the task-watchdog service and threads it through revalidateMutationScope.
  • The re-stamp is gated on the actor. Only the current run's own writes in the current execution window advance its fingerprint. Third-party writes still invalidate.
  • The benefit is that watchdogs complete multi-step recovery in one run without self-fencing. Third-party liveness fencing is preserved.

Linked Issues or Issue Description

No public GitHub issue exists for this defect. The in-PR description follows the bug-report template.

What happened?

A task-watchdog run that posts its own evidence comment is locked out of every follow-up gated mutation in the same run. The watchdog writes a comment, the comment flips the watched subtree to currentState: "live", revalidateMutationScope then returns 409 currentState: live for every subsequent mutation the run attempts, and the run ends without a disposition. The error message says "refresh the source state", but no refresh route exists, so the recovery is permanently stranded.

Expected behavior

A watchdog run should be able to complete a sanctioned multi-step recovery in one execution. The run should be able to post its evidence comment, patch the watched subtree, and resolve the recovery action. The third-party liveness guard should still block mutations when a third party has actually changed the watched subtree.

Steps to reproduce

  1. Wake a task watchdog on a stopped subtree.
  2. Let the run post its own evidence comment on a watched issue.
  3. From the same run, attempt a follow-up gated mutation such as a status patch or recovery-actions/resolve.
  4. Observe that the mutation returns 409 currentState: live or 409 stop fingerprint changed. The run ends without recording a disposition.

Paperclip version or commit

Pull request head 5cb1e1eaefe46481539aa507f84865b009c16fb7 against master 19be4cf9278b70bc151063778a94bf38bfd5c903.

Deployment mode

Self-hosted server (the bug is server-side).

Related PRs (found while searching for duplicates)

These three open PRs all address the same watchdog deadlock family with different fix shapes. This PR takes the actor-aware restamp path: the smallest patch that resolves all three observed deadlock triggers in one service function, with no schema migration, no new route, and a unit test covering the invariant. None of them is a duplicate of this one.

What Changed

  • Add restampCurrentRunAfterOwnWrites(scope, classification, observedFingerprint, ...) to server/src/services/task-watchdogs.ts. The helper re-stamps the in-flight run's stop fingerprint against the observed subtree after a write the run itself authored.
  • Thread the restamp into revalidateMutationScope in server/src/services/task-watchdog-scope.ts so gated mutations re-bind to the freshly observed scope instead of the wake-time fingerprint.
  • Wire the restamp path into the run's own evidence-comment, status-patch, and recovery-actions/resolve write paths in server/src/services/issues.ts.
  • Add server/src/__tests__/issue-watchdogs-routes.test.ts covering the three deadlock triggers and the cross-actor fencing invariant. The third-party run that flips the subtree must still be 409-ed after the restamp.
  • Diffstat: 4 files, +305 insertions, 0 deletions.

Verification

  • TypeScript compile: pnpm --filter @paperclipai/server exec tsc --noEmit introduces 0 net new errors. The PR CI job Typecheck + Release Registry is green on the rebased head.
  • Unit tests: the new issue-watchdogs-routes.test.ts cases pass under the PR General tests (server 1/5..5/5) shards.
  • PR CI green on Typecheck + Release Registry, General tests (server 1/5..5/5), General tests (workspaces-a), General tests (workspaces-b), Build, Verify serialized server suites (1/5..5/5), Canary Dry Run, e2e shard (1/3..3/3), e2e, verify, policy, security/snyk (cryppadotta), Superagent Security Scan, Socket Security: Project Report, Socket Security: Pull Request Alerts, security-review, and Contributor trust.
  • Live behavior check: with the patch applied, a single run can post its evidence comment, patch the watched subtree, and call recovery-actions/resolve in one execution without 409. A third-party run that flips the subtree still receives the 409 and cannot write past the live change.

Risks

Low risk. A buggy re-stamp could let a stale run write past a real third-party liveness change. The re-stamp is scoped to two conditions at once: (a) the current runId only, and (b) writes this run itself authored in the current execution window. Third-party writes still invalidate. The new test asserts this invariant. No schema migration, no new route, no widening of the actor-identity surface. The diff does not touch server/src/routes/issues.ts.

Model Used

Codex-built. Provider OpenAI, model codex (Cody-1, retired 2026-08-08). No extended thinking mode used. Context window used by the build was the standard Codex session context. Tool use was enabled for file edits and test execution. The patch was rebased onto current master and re-verified locally before push. Cross-model Claude verify is required by the upstream reviewer workflow (commitperclip PR Review) before merge.

Checklist

  • I have included a thinking path that traces from project context to this change
  • I have specified the model used (with version and capability details)
  • I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work
  • I have searched GitHub for duplicate or related PRs and linked them above
  • I have either (a) linked existing issues with Fixes: # / Closes: # / Refs: # OR (b) described the issue in-PR following the relevant issue template
  • I have not referenced internal/instance-local Paperclip issues or links (only public GitHub #NNN / github.com/paperclipai/paperclip URLs)
  • My branch name describes the change (e.g. docs/..., fix/...) and contains no internal Paperclip ticket id or instance-derived details
  • I have run tests locally and they pass
  • I have added or updated tests where applicable
  • I have updated relevant documentation to reflect my changes
  • I have considered and documented any risks above
  • All Paperclip CI gates are green
  • Greptile is 5/5 with no open P2s, recommendations, or follow-ups
  • I will address all Greptile and reviewer comments before requesting merge

@commitperclip

commitperclip Bot commented Aug 9, 2026

Copy link
Copy Markdown

✅ All checks passing — ready for Greptile review and maintainer approval.

— commitperclip

…rclip PR Review

Adds the five required PR-template sections (Thinking Path, What Changed,
Verification, Risks, Model Used) and the dedup-search checkbox. Re-runs
the commitperclip PR Review workflow on the rebased head.

No code change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant