mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 01:50:39 +09:00
Add accepted-plan decomposition exact-once guards and UI state (#6831)
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies, so planning approvals and child-issue fan-out are part of the core control-plane loop. > - Accepted plans are supposed to be a safe bridge from planning into execution, especially when agents wake from review decisions and reuse isolated workspaces. > - The duplicate-subtask incident showed that an accepted plan revision could be interpreted more than once across overlapping runs, which broke the single-source-of-truth model for issue decomposition. > - Fixing that required tightening the backend contract first: accepted-plan decomposition needs an exact-once fingerprint, durable claim state, and retry-safe child creation. > - Once that backend behavior existed, the board still needed visibility into what happened, so the issue detail view needed a dedicated decomposition section instead of forcing operators to reconstruct child creation from raw activity. > - This pull request adds the exact-once decomposition primitive, hardens wake routing and regressions around the incident, and surfaces decomposition state in the UI so future incidents are both prevented and easier to inspect. ## What Changed - Added accepted-plan decomposition semantics to `doc/execution-semantics.md`, including the exact-once fingerprint, durable claim/result expectations, and retry/resume behavior. - Added persistent accepted-plan decomposition claims in the backend, including schema, shared types/validators, service logic, and issue routes for creating and listing decomposition state. - Hardened heartbeat routing so an accepted-plan continuation stays scoped to the relevant planning issue instead of opportunistically re-decomposing another accepted issue on the same assignee. - Added regression coverage for the original failure modes: concurrent same-parent retries, cross-issue accepted-plan isolation, and partial child recreation under the same fingerprint. - Added the `Plan decomposition` issue-detail section plus supporting API/query-key/activity formatting updates so operators can see revision status, owner, child counts, and the linked child issues directly in the UI. - Included the small follow-up UI fix so the decomposition section still renders when the issue work mode is no longer `planning`. ## Verification - `pnpm --filter @paperclipai/server typecheck` - `pnpm --filter @paperclipai/ui typecheck` - `pnpm --filter @paperclipai/db typecheck` - `pnpm exec vitest run server/src/__tests__/issues-service.test.ts` - `pnpm exec vitest run server/src/__tests__/issues-service.test.ts -t "lists persisted decompositions with child issue summaries"` - `pnpm exec vitest run server/src/__tests__/issues-service.test.ts -t "accepted plan decomposition" server/src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts server/src/__tests__/heartbeat-context-summary.test.ts` - Manual UI path: create a planning issue without an isolated execution workspace, add a `plan` document, accept the `request_confirmation`, let Paperclip create child issues, then reopen the parent issue detail page and confirm the `Plan decomposition` section shows the accepted revision, status, idempotent-claim badge, and child links. - Separate follow-up bug noted during manual UI validation: accepting a plan on an issue whose run never records `workspace_finalize` is tracked in `PAPA-445` and is not part of this PR’s fix scope. ## Risks - This adds a new migration and a large Drizzle snapshot update; reviewers should confirm the schema shape and generated metadata match the intended decomposition table. - The exact-once claim changes sit on the accepted-plan fan-out path, so regressions there could block legitimate child creation or mis-handle retries if the claim state machine is wrong. - The new UI only appears when decomposition records exist; reviewers should use the manual verification path above rather than expecting existing issues on a stale local instance to show the section automatically. - `PAPA-445` remains an open follow-up for the `workspace_finalize` accept gate when a planning handoff never records finalize; that bug can interfere with reproducing the UI flow on isolated workspaces but does not change the correctness of the exact-once decomposition feature itself. > Checked `ROADMAP.md`: this PR is a bug fix / control-plane hardening change for accepted-plan decomposition, not a new uncoordinated roadmap feature. ## Model Used - OpenAI Codex via Paperclip `codex_local` (GPT-5-based coding agent; exact backend model ID/context window not exposed in the run context), with repository tool use, shell execution, and code-editing capabilities. <img width="806" height="1069" alt="Screenshot 2026-05-27 at 11 05 48 PM" src="https://github.com/user-attachments/assets/5b00b670-96cd-4470-b0a3-581743bcae28" /> ## 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 run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
9eac727cf1
commit
d9f91576a0
32 changed files with 22308 additions and 16 deletions
|
|
@ -1,7 +1,7 @@
|
|||
# Execution Semantics
|
||||
|
||||
Status: Current implementation guide
|
||||
Date: 2026-04-26
|
||||
Date: 2026-05-23
|
||||
Audience: Product and engineering
|
||||
|
||||
This document explains how Paperclip interprets issue assignment, issue status, execution runs, wakeups, parent/sub-issue structure, and blocker relationships.
|
||||
|
|
@ -152,7 +152,73 @@ Blocked issues should stay idle while blockers remain unresolved. Paperclip shou
|
|||
|
||||
If a parent is truly waiting on a child, model that with blockers. Do not rely on the parent/child relationship alone.
|
||||
|
||||
## 7. Non-Terminal Issue Liveness Contract
|
||||
## 7. Accepted-Plan Decomposition
|
||||
|
||||
An accepted plan confirmation is permission to decompose one specific accepted plan revision into child issues.
|
||||
|
||||
This complements the existing accepted-plan continuation rule: once a plan is accepted, the source issue may create child implementation issues, but it must not start implementation work on the source issue itself during that continuation.
|
||||
|
||||
Paperclip must treat accepted-plan decomposition as an exact-once control-plane primitive, not as a free-floating wake that any later run may interpret again.
|
||||
|
||||
### Exact-once fingerprint
|
||||
|
||||
The canonical decomposition fingerprint is:
|
||||
|
||||
- `(sourceIssueId, acceptedPlanRevisionId)`
|
||||
|
||||
Where:
|
||||
|
||||
- `sourceIssueId` is the issue whose `plan` document revision was accepted
|
||||
- `acceptedPlanRevisionId` is the accepted `plan` document revision
|
||||
|
||||
This is the product contract because the accepted revision is the thing being authorized for decomposition. Re-accepting, re-waking, or re-reading the same accepted revision must not authorize a second child tree. A later accepted revision on the same source issue is a new fingerprint and may produce a different decomposition result.
|
||||
|
||||
An implementation may also store the accepted interaction id, acceptance run id, or other evidence, but those values must collapse onto the same uniqueness guarantee. They must not allow a second decomposition claim for the same `(sourceIssueId, acceptedPlanRevisionId)` pair.
|
||||
|
||||
### Durable claim and durable result
|
||||
|
||||
Before creating child issues, the first decomposition attempt must create or reuse a durable record for the fingerprint.
|
||||
|
||||
That durable record must be able to answer, without reconstructing the thread from comments or transcripts:
|
||||
|
||||
- whether decomposition for the fingerprint is `in_flight` or `completed`
|
||||
- which run or owner currently holds the in-flight claim
|
||||
- which child issues, if any, have already been created under that fingerprint
|
||||
- which final child issue ids belong to the completed result
|
||||
|
||||
Paperclip does not need to mandate a specific storage shape in this document. The record may live in a dedicated table, source-issue execution state, interaction metadata, or another durable product surface. What matters is the contract:
|
||||
|
||||
- the claim is durable before fan-out starts
|
||||
- partial progress is durable while fan-out is underway
|
||||
- the completed child result set is durable after fan-out finishes
|
||||
|
||||
If a run creates some children and then dies, retries must continue from the same fingerprint and reuse the already-recorded partial result. They must not restart decomposition as if nothing happened.
|
||||
|
||||
### Parent live path while decomposition is in flight
|
||||
|
||||
While decomposition for an accepted fingerprint is incomplete, the source issue must expose an explicit live path for that same fingerprint.
|
||||
|
||||
The accepted interaction by itself is only evidence that the plan was approved. It is not a sufficient live path once decomposition begins. The source issue must make it clear what moves the fingerprint forward next, such as:
|
||||
|
||||
- the active decomposition run
|
||||
- a queued continuation wake for the same assignee
|
||||
- a monitor or explicit recovery action tied to the same decomposition claim
|
||||
- a blocked state that names the real blocker for finishing that claimed decomposition
|
||||
|
||||
If the live run disappears, Paperclip must repair, resume, or visibly block the existing claim. It must not leave the source issue in a state where a second run can interpret the same acceptance as fresh permission to create sibling issues again.
|
||||
|
||||
### Concurrent and repeat attempts
|
||||
|
||||
Every later run that encounters the same accepted-plan fingerprint must consult the durable claim/result before creating children.
|
||||
|
||||
- If no claim exists, the run may atomically create the claim and become the decomposition owner.
|
||||
- If a claim exists and is `in_flight`, the later run must reuse that claim. It may resume the same decomposition if it is the valid continuation owner, or it may exit after observing that another run already owns the work.
|
||||
- If a claim exists and is `completed`, the later run must reuse the recorded child result and must not create new sibling issues.
|
||||
- If the prior attempt ended after partial child creation, the retry must continue under the same fingerprint and preserve the already-created child ids.
|
||||
|
||||
Concurrent accepted-plan runs are therefore idempotent relative to the fingerprint. Creating multiple child trees for the same `(sourceIssueId, acceptedPlanRevisionId)` pair is a product bug.
|
||||
|
||||
## 8. Non-Terminal Issue Liveness Contract
|
||||
|
||||
For agent-owned, non-terminal issues, Paperclip should never leave work in a state where nobody is responsible for the next move and nothing will wake or surface it.
|
||||
|
||||
|
|
@ -292,13 +358,13 @@ A blocker chain is covered only when its unresolved leaf is live or explicitly w
|
|||
|
||||
A `blocked` issue is stalled when the unresolved blocker leaf has no active run, queued wake, typed participant, pending interaction or approval, user owner, external owner/action, or recovery action. In that case the parent should show the first stalled leaf instead of presenting the dependency as calmly covered.
|
||||
|
||||
## 8. Crash and Restart Recovery
|
||||
## 9. Crash and Restart Recovery
|
||||
|
||||
Paperclip now treats crash/restart recovery as a stranded-assigned-work problem, not just a stranded-run problem.
|
||||
|
||||
There are two distinct failure modes.
|
||||
|
||||
### 8.1 Stranded assigned `todo`
|
||||
### 9.1 Stranded assigned `todo`
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -314,7 +380,7 @@ Recovery rule:
|
|||
|
||||
This is a dispatch recovery, not a continuation recovery.
|
||||
|
||||
### 8.2 Stranded assigned `in_progress`
|
||||
### 9.2 Stranded assigned `in_progress`
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -330,13 +396,13 @@ Recovery rule:
|
|||
|
||||
This is an active-work continuity recovery.
|
||||
|
||||
### 8.3 Recovery model-profile lane
|
||||
### 9.3 Recovery model-profile lane
|
||||
|
||||
Cheap model profiles are only for status-only operational recovery overhead. Paperclip may request `modelProfile: "cheap"` for bounded recovery-owner work that updates task liveness, clears bad status, records a disposition, or asks for human/manager intervention. Those wakes must carry guard context such as `allowDeliverableWork: false`, `allowDocumentUpdates: false`, and `resumeRequiresNormalModel: true`.
|
||||
|
||||
Automatic retries that can continue source work must use the original/normal model lane. This includes failed source-work retries, process-loss retries, transient/scheduled retries, max-turn continuations, source-assignee continuations, assigned-todo dispatch recovery, and any run that can update repo files, issue documents, plans, work products, or attachments. When a cheap status-only recovery determines that actual work remains, it must hand back to a normal-model worker run before source work or persistent deliverable updates resume. Cheap recovery hints must be scrubbed from copied retry, resume, child, and downstream source-work contexts.
|
||||
|
||||
## 9. Startup and Periodic Reconciliation
|
||||
## 10. Startup and Periodic Reconciliation
|
||||
|
||||
Startup recovery and periodic recovery are different from normal wakeup delivery.
|
||||
|
||||
|
|
@ -350,7 +416,7 @@ On startup and on the periodic recovery loop, Paperclip now does five things in
|
|||
|
||||
The stranded-work pass closes the gap where issue state survives a crash but the wake/run path does not. The silent-run scan covers the separate case where a live process exists but has stopped producing observable output. The productivity-review pass is later and separate; it reviews unusual progression patterns on assigned source issues, not stale run handles after a source issue already has a valid disposition.
|
||||
|
||||
## 10. Silent Active-Run Watchdog
|
||||
## 11. Silent Active-Run Watchdog
|
||||
|
||||
An active run can still be unhealthy even when its process is `running`. Paperclip treats prolonged output silence as a watchdog signal, not as proof that the run is failed.
|
||||
|
||||
|
|
@ -402,7 +468,7 @@ This is distinct from productivity review. Productivity review asks whether an a
|
|||
|
||||
Detached process cleanup is operational hygiene, not source issue liveness. Cleanup should be best-effort and auditable. If cleanup fails but the source issue is already terminal with same-run durable evidence, Paperclip should preserve the cleanup failure on the run/watchdog audit trail and route only the cleanup concern to bounded recovery when a real owner/action remains.
|
||||
|
||||
## 11. Auto-Recover vs Explicit Recovery vs Human Escalation
|
||||
## 12. Auto-Recover vs Explicit Recovery vs Human Escalation
|
||||
|
||||
Paperclip uses three different recovery outcomes, depending on how much it can safely infer.
|
||||
|
||||
|
|
@ -446,7 +512,7 @@ Examples:
|
|||
|
||||
In these cases Paperclip should leave a visible issue/comment trail instead of silently retrying.
|
||||
|
||||
## 12. What This Does Not Mean
|
||||
## 13. What This Does Not Mean
|
||||
|
||||
These semantics do not change V1 into an auto-reassignment system.
|
||||
|
||||
|
|
@ -463,7 +529,7 @@ The recovery model is intentionally conservative:
|
|||
- open an explicit recovery action when the system can identify a bounded recovery owner/action
|
||||
- escalate visibly when the system cannot safely keep going
|
||||
|
||||
## 13. Practical Interpretation
|
||||
## 14. Practical Interpretation
|
||||
|
||||
For a board operator, the intended meaning is:
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue