Skip to content

fix(work-loop): open a PR for work recovered from a prior run (crash recovery)#84

Merged
OGtwelve merged 1 commit into
mainfrom
fix/crash-recovery-partb-77
Jun 30, 2026
Merged

fix(work-loop): open a PR for work recovered from a prior run (crash recovery)#84
OGtwelve merged 1 commit into
mainfrom
fix/crash-recovery-partb-77

Conversation

@OGtwelve

@OGtwelve OGtwelve commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes the crash-recovery story started in #77 (Part A reused the branch on resume so prior commits persist). A group whose earlier tasks were committed by a prior run but never opened a PR — the run crashed, or a remaining task blocks on resume — now recovers that committed work into a PR instead of stranding it as blocked.

Changes

  • src/loop/work-loop.ts — new recoveredDelivery(group) (exported for tests): synthesizes a delivery from the group's already-done tasks (a reliable "committed" proxy — a task is marked done only after its commit is finalized), anchored on the group branch + title. work() uses it on both block paths when no PR exists. A fresh run whose first task blocks (nothing done) still blocks.

Testing

  • src/loop/work-loop.test.ts: resume-with-all-done → recovers a PR; resume-with-a-blocked-task-after-a-committed-one → recovers a PR; fresh-first-task-blocked → still blocks; recoveredDelivery unit cases. bun test + node --test green (42 file / 683 suite); biome + tsc clean.

Closes #77

Summary by CodeRabbit

  • New Features

    • Resumed work can now be turned into a PR automatically when some tasks were already completed.
  • Bug Fixes

    • Groups no longer stay blocked if there is recoverable completed work from a previous run.
    • A blocked result now only leaves the group blocked when there is truly nothing to recover.
    • Added broader test coverage for recovery and blocked-run behavior.

…recovery)

Completes #77. With branch reuse on resume (Part A), a group whose earlier tasks were committed by a prior run but never opened a PR — because the run crashed, or a remaining task blocks on resume — now recovers that committed work into a PR instead of stranding it as blocked.

recoveredDelivery() synthesizes a delivery from the group's already-`done` tasks (a reliable proxy for committed, since a task is only marked done after its commit is finalized) anchored on the group branch + title; work() uses it on both block paths when no PR exists. A genuinely fresh run whose first task blocks (nothing done) still blocks.

Closes #77
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: c71edaf6-c0e8-40a2-944b-b5188f2c2e29

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough >WorkLoop: blocked result
WorkLoop->>recoveredDelivery: recoveredDelivery(group)
alt recovery available
  recoveredDelivery-->>WorkLoop: WorkerDelivery
  WorkLoop->>ctx: set ctx.delivery
  WorkLoop-->>WorkLoop: return ok (opens PR)
else no recovery
  WorkLoop-->>WorkLoop: set blockedReason
end

else worker returns null delivery
worker-->>WorkLoop: delivery=null
WorkLoop->>recoveredDelivery: recoveredDelivery(group)
alt recovery available
recoveredDelivery-->>WorkLoop: WorkerDelivery
WorkLoop-->>WorkLoop: return ok (opens PR)
else no recovery
WorkLoop-->>WorkLoop: blocked (no PR opened)
end
end

</diagram>
</layer>
<layer id="tests" title="Work-loop and recoveredDelivery tests" depends_on="wiring">
<summary>Replaces the prior blocked-all-done test with recovery-focused scenarios, adds mixed recovered/blocked test, and adds a direct unit test for recoveredDelivery null/non-null conditions.</summary>
<ranges>
range_c1f2c05591d0
range_cd4193766f9e
range_375f85a8a4e5
range_3c9d8ef824dd
range_6f9a8d097dcb
</ranges>
</layer>
</cohort>
<unassigned_ranges>
</unassigned_ranges>
</review_stack_artifact-->

## Walkthrough

Adds a new exported `recoveredDelivery(group)` helper to `work-loop.ts` that synthesizes a `WorkerDelivery` from previously committed (`done`) tasks when no PR exists. The work-loop's `buildStageDeps().work` bridge is updated to call this helper in two paths—worker-blocked and null-delivery—so resumed runs open a PR instead of entering blocked state. Tests are updated accordingly.

## Changes

**Crash-resume PR recovery via `recoveredDelivery`**

| Layer / File(s) | Summary |
|---|---|
| **`recoveredDelivery` helper** <br> `packages/aitm/src/loop/work-loop.ts` | New exported `recoveredDelivery(group)` returns a synthetic `WorkerDelivery` with `progressEntries` from done tasks and empty `changes`, or `null` when a PR already exists or no tasks are done. |
| **Work-loop blocked/no-delivery wiring** <br> `packages/aitm/src/loop/work-loop.ts` | `buildStageDeps().work` now calls `recoveredDelivery` when a worker yields `blocked` or `null` delivery; routes to `ok`+PR-open instead of `blockedReason` when recovery is non-null. |
| **Tests** <br> `packages/aitm/src/loop/work-loop.test.ts` | Replaces the prior all-done→blocked test with recovery scenarios (all-done opens PR, fresh group stays blocked, mixed committed/undone recovers to PR), plus a direct unit test for `recoveredDelivery`. |

## Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

## Possibly related issues

- **`#77` (Partial group work can be stranded on crash-recovery)**: This PR directly addresses the issue by introducing `recoveredDelivery` to reconstruct a delivery from persisted `done` task state on resume, allowing the loop to open a PR for committed-but-unpushed work instead of leaving the group blocked.
- **`#71`**: The in-pass partial-delivery fix (opening a PR when a later task blocks mid-run) is the same pattern extended here to the cross-run crash-recovery case via `recoveredDelivery`.

## Possibly related PRs

- [developerz-ai/ai-task-master#74](https://github.com/developerz-ai/ai-task-master/pull/74): Both PRs modify `work-loop.ts` and `work-loop.test.ts` to open a PR for committed earlier work instead of leaving a group blocked when a task returns `blocked`.
- [developerz-ai/ai-task-master#61](https://github.com/developerz-ai/ai-task-master/pull/61): Established the stage-based PR lifecycle that `buildStageDeps().work` extends here with the `recoveredDelivery` recovery paths.
- [developerz-ai/ai-task-master#10](https://github.com/developerz-ai/ai-task-master/pull/10): Shares core `work-loop` logic around when `awaiting-pr` vs `blocked` outcomes are produced from worker results.

</details>

<!-- walkthrough_end -->
<!-- pre_merge_checks_walkthrough_start -->

<details>
<summary>🚥 Pre-merge checks | ✅ 5</summary>

<details>
<summary>✅ Passed checks (5 passed)</summary>

|         Check name         | Status   | Explanation                                                                                                                                |
| :------------------------: | :------- | :----------------------------------------------------------------------------------------------------------------------------------------- |
|      Description Check     | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                |
|         Title check        | ✅ Passed | The title clearly matches the main change: crash-recovery now opens a PR for previously recovered work in work-loop.                       |
|     Linked Issues check    | ✅ Passed | The changes implement `#77` by reconstructing recoverable delivery from done tasks and opening a PR instead of leaving resumed work blocked. |
| Out of Scope Changes check | ✅ Passed | The PR stays focused on crash-recovery logic and its tests, with no clear unrelated code changes.                                          |
|     Docstring Coverage     | ✅ Passed | Docstring coverage is 66.67% which is sufficient. The required threshold is 30.00%.                                                        |

</details>

</details>

<!-- pre_merge_checks_walkthrough_end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

<details>
<summary>🧪 Generate unit tests (beta)</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-4839885643"} -->   Create PR with unit tests
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-4839885643"} -->   Commit unit tests in branch `fix/crash-recovery-partb-77`

</details>

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---




<sub>Comment `@coderabbitai help` to get the list of available commands.</sub>

<!-- tips_end -->

@OGtwelve

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@OGtwelve

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@OGtwelve OGtwelve merged commit 19515a5 into main Jun 30, 2026
3 checks passed
@OGtwelve OGtwelve deleted the fix/crash-recovery-partb-77 branch June 30, 2026 04:36
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.

Partial group work can be stranded on crash-recovery (committed-but-unpushed tasks)

1 participant