# fix(review): persist findings even when finalize is cancelled

`80dd3d8`→[main](/content/gh/entireio/cli/commits/main/index.html)·

dipree·2w ago·2 files·+47 added/-0 removed

When the user Ctrl+C's out of a slow final-report/synthesis step, the run
context is cancelled AFTER summary.Cancelled was computed for already-
finished workers. The summary.Cancelled guard therefore passes, but the
git/disk work in writePostReviewManifest then ran on the cancelled
context and failed with "write to disk failed: resolve git common dir:
git rev-parse: context canceled" — silently discarding findings the user
waited minutes for.

Detach persistence from the run context (context.WithoutCancel) with its
own 30s deadline so completed findings survive a cancel during finalize.
A genuinely cancelled run still skips via the existing summary.Cancelled
guard.

## Sessions

f565e1556184View transcript

## Changes

2

- cmd/entire/cli/review

- Mcmd.go+11

- Mmanifest_test.go+36

```
1442 unmodified lines

1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459

1442 unmodified lines

if summary.Cancelled || len(summary.AgentRuns) == 0 {
		return
	}

// The findings were produced by worker sessions that already finished. If
	// the user Ctrl+C's out of a slow final-report/synthesis step, the run
	// context is cancelled AFTER summary.Cancelled was computed (so the guard
	// above doesn't fire), and the git/disk work below would fail with
	// "context canceled" — silently discarding findings the user waited
	// minutes for. Detach from the run context and give persistence its own
	// short deadline so completed findings survive a cancel during finalize.
	ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 30*time.Second)
	defer cancel()

manifest, states, err := localReviewManifestFromCurrentState(ctx, worktreeRoot, headSHA, summary, aggregateOutput)
	if err != nil {
		logging.Debug(ctx, "review manifest not written", slog.String("error", err.Error()))
```

Mcmd/entire/cli/review/cmd.go+11

```
331 unmodified lines

332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

331 unmodified lines

}
}

// TestWritePostReviewManifest_SurvivesCancelledRunContext pins the fix for
// findings being silently discarded when the user Ctrl+C's out of a slow
// final-report step: the workers already finished (summary not cancelled), so
// persistence must run on a detached context and not fail with
// "context canceled" just because the run context was cancelled afterward.
func TestWritePostReviewManifest_SurvivesCancelledRunContext(t *testing.T) {
	repoRoot := t.TempDir()
	testutil.InitRepo(t, repoRoot)
	t.Chdir(repoRoot)

// Simulate the run context cancelled during finalization.
	ctx, cancel := context.WithCancel(context.Background())
	cancel()

var out strings.Builder
	summary := reviewtypes.RunSummary{
		StartedAt: time.Now(),
		AgentRuns: []reviewtypes.AgentRun{
			{Name: "claude-code", Status: reviewtypes.AgentStatusSucceeded},
		},
	}

writePostReviewManifest(ctx, &out, repoRoot, "abc123", summary, "")

got := out.String()
	// The git/disk work must have run despite the cancelled input context: we
	// reach the normal "no matching sessions" path, not a context-canceled
	// failure.
	if strings.Contains(got, "context canceled") {
		t.Fatalf("persistence used the cancelled run context; findings would be lost:\n%s", got)
	}
	if !strings.Contains(got, "no session states found") {
		t.Fatalf("expected persistence to proceed to the no-session-state path; got:\n%s", got)
	}
}

func TestExplainEmptyManifest_NoStates(t *testing.T) {
	t.Parallel()
	summary := reviewtypes.RunSummary{
