Skip to content

feat: review watch - #133

Merged
pedronauck merged 10 commits into
mainfrom
pn/review-watch
Apr 30, 2026
Merged

pedronauck merged 10 commits into
mainfrom
pn/review-watch

Conversation

@pedronauck

@pedronauck pedronauck commented Apr 30, 2026 •

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added "review watch" (daemon-run mode with streaming events, optional auto-push) and CLI command reviews watch; new API/CLI to start and stream watch runs
    • Review round detail page and API to fetch a single review round
  • Improvements

    • Completed workflows section and richer workflow summaries (task counts, start gating, archive eligibility)
    • Added watch_reviews config and four review-watch extension hooks; expanded review event kinds and payloads
    • CLI sync output now reports "Stale workflows pruned" and improved API error-message selection and sync-result formatting
  • UI/UX

    • Layout/truncation fixes, updated navigation links, card/header sizing adjustments

@pedronauck pedronauck self-assigned this Apr 30, 2026
@coderabbitai

coderabbitai Bot commented Apr 30, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6b9d108b-60fc-419e-9c85-974d94c3e25b

📥 Commits

Reviewing files that changed from the base of the PR and between c051a8c and bca9e8e.

⛔ Files ignored due to path filters (2)
  • .compozy/tasks/reviews-watch/reviews-004/issue_001.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-004/issue_002.md is excluded by !**/*.md
📒 Files selected for processing (1)
  • internal/core/sync_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/core/sync_test.go

Walkthrough

Adds a new "review watch" feature: API contract and route, client and handler, daemon run-mode and git/provider orchestration, CLI command, extension hooks, DB pruning/eligibility and task counts, frontend round views, event kinds/payloads, and extensive tests across core, daemon, CLI, SDK, and UI layers.

Changes

Cohort / File(s) Summary
API Contract & Routes
internal/api/contract/types.go, internal/api/contract/routes.go
Add ReviewWatchRequest type and POST /api/reviews/:slug/watch route; extend sync and workflow summary types with pruning and task-count fields.
API Client & Handlers
internal/api/client/reviews_exec.go, internal/api/client/reviews_exec_test.go, internal/api/core/handlers.go, internal/api/core/handlers_contract_test.go, internal/api/core/handlers_service_errors_test.go, internal/api/core/routes.go, internal/api/core/interfaces.go
Add Client.StartReviewWatch and Handlers.StartReviewWatch, wire route and interface StartWatch, and add tests for success/error/contract behavior.
Daemon Run Manager & Watch Core
internal/daemon/review_watch.go, internal/daemon/review_watch_test.go, internal/daemon/run_manager.go, internal/daemon/run_manager_test.go
Implement RunManager.StartReviewWatch run-mode, concurrency reservation, provider polling, child fix-run orchestration, push flow, hook lifecycle, persistence, and large test suite; wire run-mode into run manager.
Daemon Transport, Sync & Git
internal/daemon/review_exec_transport_service.go, internal/daemon/review_exec_transport_service_test.go, internal/daemon/review_watch_git.go, internal/daemon/review_watch_git_test.go, internal/daemon/review_watch_hooks.go
Add transport StartWatch, fetch/sync helpers, ReviewWatchGit abstraction (state + push), hook dispatch/validation and post-write problem wrapping, plus unit tests.
Provider Layer & CodeRabbit
internal/core/provider/provider.go, internal/core/provider/coderabbit/coderabbit.go, internal/core/provider/coderabbit/*, internal/core/provider/overlay.go, tests
Introduce WatchStatus types/interface; implement CodeRabbit WatchStatus (including working-dir option), overlay delegation, and provider tests.
Core Fetch/Sync/Reviews
internal/core/fetch.go, internal/core/fetch_test.go, internal/core/sync.go, internal/core/reviews/store.go, internal/core/reviews/store_test.go, internal/core/model/workflow_ops.go
Refactor fetch/write phases, add fetch-only/write-only helpers, stop legacy front-matter fallback, add pruning collection during sync, and extend sync result/model fields.
GlobalDB Pruning & Archive Eligibility
internal/store/globaldb/sync.go, internal/store/globaldb/archive.go, internal/store/globaldb/sync_test.go, internal/store/globaldb/archive_test.go
Add PruneMissingActiveWorkflows, bulk WorkflowArchiveEligibilityByIDs with ReviewIssueTotal, pruning/skipped reporting types, and tests validating prune/archival behavior for review-only workflows.
CLI: reviews watch
internal/cli/reviews_exec_daemon.go, internal/cli/reviews_exec_daemon_additional_test.go, internal/cli/*, internal/cli/reviews_exec_test_helpers_test.go
Add reviews watch command, flags (poll, timeout, quiet, auto-push, push target, until-clean, max-rounds), validation, runtime override handling, attach/format rules, daemon client wiring, and tests.
Extensions & Hooks (Go & TS SDKs)
sdk/extension/*, sdk/extension-sdk-ts/*, sdk/extension/, tests
Add four review-watch hook names and payload/patch types; register mutable/observer handlers in Go and TS SDKs; enforce mutability rules and add tests.
Events & Payloads
pkg/compozy/events/*
Add 10 review-watch event kinds and ReviewWatchPayload structure; update JSON compatibility/docs tests.
Frontend: reviews rounds & UI
web/src/systems/reviews/*, web/src/routes/*, storybook, e2e, tests
Add round-detail route/component (ReviewRoundDetailView), useReviewRound + adapter getReviewRound, simplify index to round cards, update navigation/links, storybook stories, e2e fixtures, and many tests.
Frontend: misc UI & helpers
packages/ui/*, web/src/*
Styling tweaks, new sync-result formatter, dashboard/workflow inventory completed section, query-key helpers, and related tests.
DB Read Queries & Task Counts
internal/store/globaldb/read_queries.go, internal/store/globaldb/read_queries_test.go
Add TaskCountsByWorkflowIDs and WorkflowTaskCountsRow for bulk task-count reads; tests for aggregated read behaviors.
Various tests & infra
multiple *_test.go and fixtures across repo
Extensive new and updated tests across core, daemon, CLI, provider, SDKs, frontend, and DB layers validating watch flows, hooks, git, pruning, archive behavior, and UI rendering.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI (reviews watch)
  participant API as Daemon API
  participant RunMgr as RunManager
  participant Provider as Provider
  participant Git as ReviewWatchGit
  participant DB as GlobalDB

  CLI->>API: POST /api/reviews/:slug/watch (ReviewWatchRequest)
  API->>RunMgr: StartReviewWatch(ctx, workspace, slug, req)
  RunMgr->>Provider: Init registry & WatchStatus polling
  loop Polling loop
    Provider->>RunMgr: WatchStatus (Pending / CurrentReviewed / Stale)
    RunMgr->>Git: State() (branch, head, dirty, unpushed)
    alt Provider reports current-reviewed
      RunMgr->>RunMgr: Start child fix run (with runtime overrides)
      RunMgr->>DB: persist child run status/events
      opt auto-push enabled
        RunMgr->>Git: Push(remote, branch)
      end
      RunMgr->>DB: persist round outcome/events
    else still pending
      RunMgr->>DB: emit waiting event
      RunMgr->>RunMgr: wait poll interval
    end
  end
  RunMgr->>DB: finalize parent run (completed/cancelled/failed)
  RunMgr-->>API: return RunResponse (201 Created)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • feat: daemon web UI #122: Related — touches API routes and review-related endpoints overlapping the new POST /api/reviews/:slug/watch contract and transport changes.
  • refactor: daemon improvs #121: Related — modifies review client/handler and daemon run infrastructure extended here with StartReviewWatch and run-mode wiring.
  • fix: daemons adjustments #116: Related — adjusts CLI reviews command wiring and behavior; overlaps with the new reviews watch CLI flags, validation, and preflight logic.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pn/review-watch

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🧹 Nitpick comments (12)
web/src/systems/runs/components/runs-list-view.test.tsx (1)

141-144: ⚡ Quick win

Add an assertion for the new status-badge non-shrinking behavior.

This test already checks truncation and status text; asserting shrink-0 on the badge would directly guard the layout regression fix from this PR.

Proposed test assertion
     const link = screen.getByTestId(`runs-list-link-${longRunId}`);
     expect(link).toHaveClass("truncate");
-    expect(screen.getByTestId(`runs-list-status-${longRunId}`)).toHaveTextContent("running");
+    const status = screen.getByTestId(`runs-list-status-${longRunId}`);
+    expect(status).toHaveTextContent("running");
+    expect(status).toHaveClass("shrink-0");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/runs/components/runs-list-view.test.tsx` around lines 141 -
144, Add an assertion to the test that verifies the status badge does not
shrink: locate the status element retrieved by
getByTestId(`runs-list-status-${longRunId}`) in the runs-list-view test and add
an expectation that it includes the "shrink-0" class (e.g.,
expect(screen.getByTestId(`runs-list-status-${longRunId}`)).toHaveClass("shrink-0")).
This will complement the existing truncation and text assertions and guard the
non-shrinking layout behavior.
internal/core/reviews/store.go (1)

211-214: ⚡ Quick win

Wrap metadata extraction errors with call-site context.

Returning raw err here reduces diagnosability for this boundary. Keep %w wrapping so upstream matching still works.

Proposed fix
 	meta, err := roundMetaFromIssueFrontMatter(entries)
 	if err != nil {
-		return model.RoundMeta{}, err
+		return model.RoundMeta{}, fmt.Errorf("snapshot round meta from issue front matter: %w", err)
 	}

As per coding guidelines, "Prefer explicit error returns with wrapped context using fmt.Errorf("context: %w", err)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/core/reviews/store.go` around lines 211 - 214, The call to
roundMetaFromIssueFrontMatter returns raw err which is returned directly; wrap
this error with call-site context using fmt.Errorf to preserve `%w` semantics so
upstream matching still works. Update the error return in the block that calls
roundMetaFromIssueFrontMatter (the variables meta, err) to return a wrapped
error like fmt.Errorf("extracting round meta from issue front matter: %w", err),
ensuring you import fmt if not already imported.
sdk/extension/internal_test.go (1)

241-242: ⚡ Quick win

Use Should... subtest names to match required test style.

The new subtests use raw hook names; please rename them to the Should... convention required for test cases.

Proposed change
-		t.Run(string(tc.hook), func(t *testing.T) {
+		t.Run("Should classify "+string(tc.hook)+" mutability correctly", func(t *testing.T) {

As per coding guidelines: MUST use t.Run("Should...") pattern for ALL test cases.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/extension/internal_test.go` around lines 241 - 242, Rename the subtest
name used in the t.Run call from the raw hook value to the required "Should..."
pattern: replace t.Run(string(tc.hook), ...) with a name that prefixes the hook
with "Should " (e.g., "Should "+string(tc.hook) or fmt.Sprintf("Should %s",
tc.hook)); update the t.Run invocation that currently wraps t.Parallel() so all
subtests follow the "Should..." naming convention (adjust imports if you choose
fmt.Sprintf).
web/src/systems/reviews/components/review-round-detail-view.test.tsx (1)

99-108: ⚡ Quick win

Reuse one render tree for the state transitions.

Each renderRoundDetail(...) call mounts a fresh router without unmounting the previous one, so the later assertions run against cumulative DOM. That makes this test brittle to duplicate test ids and leaked router subscriptions. Prefer rerender(...) or cleanup() between scenarios.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/reviews/components/review-round-detail-view.test.tsx` around
lines 99 - 108, The test currently calls renderRoundDetail multiple times
causing cumulative DOM; update the test to reuse a single render tree by either
calling cleanup() between renderRoundDetail invocations or by using the rerender
function returned from the initial renderRoundDetail call to update props/state;
modify the "Should render loading, error, empty, and refreshing states" test to
call renderRoundDetail once, then use rerender(...) to switch to { issues: [],
issuesError: "boom" } and then to { issues: [] }, or call cleanup() before each
subsequent renderRoundDetail, ensuring assertions (screen.getByTestId(...)) run
against a single, non-leaking render tree.
internal/core/sync_test.go (1)

573-600: 💤 Low value

Subtest naming could follow the "Should..." convention.

The coding guidelines recommend using t.Run("Should...") pattern for test cases. Current names like "consistent provider and pr" could be rephrased as "Should project metadata when provider and pr are consistent".

This is a minor style suggestion that doesn't affect test functionality.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/core/sync_test.go` around lines 573 - 600, Rename the subtest label
to follow the "Should..." convention: in the t.Run call inside the Test (the
subtest that writes files and calls collectReviewRounds), change the name string
from "consistent provider and pr" to something like "Should project metadata
when provider and pr are consistent" so the test description follows the
project's naming guideline while leaving the logic (writeSyncWorkflowFile calls,
collectReviewRounds, and assertions on rounds[0].Provider, rounds[0].PRRef,
ResolvedCount, UnresolvedCount) unchanged.
web/src/systems/reviews/components/review-detail-view.tsx (1)

286-294: 💤 Low value

Consider whether removing all underscores is intentional.

The displayReviewTitle function removes all underscores with .replace(/_/g, ""), which might unintentionally mangle technical identifiers in review titles (e.g., "Fix my_variable_name" becomes "Fix myvariablename").

If the intent is to convert snake_case to spaces, consider replacing underscores with spaces instead:

💡 Suggested alternative
 const cleaned = raw
   .replace(/(?:[\u{1F300}-\u{1FAFF}\u2600-\u27BF]|\uFE0F)/gu, "")
   .replace(/\s*\|\s*/g, " - ")
-  .replace(/_/g, "")
+  .replace(/_/g, " ")
   .replace(/\s+/g, " ")
   .trim();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/reviews/components/review-detail-view.tsx` around lines 286 -
294, The displayReviewTitle function currently removes all underscores via the
.replace(/_/g, "") call which can mangle snake_case identifiers; update
displayReviewTitle to replace underscores with spaces (e.g., replace /_/g with "
" or a regex that collapses consecutive underscores to a single space), then
collapse multiple whitespace (.replace(/\s+/g, " ")) and trim as already done so
titles like "Fix my_variable_name" become "Fix my variable name" while
preserving spacing and readability.
internal/daemon/review_watch_hooks.go (1)

239-261: 💤 Low value

Consider adding debug logging for nil-guard early returns.

The nil checks on lines 244-246 silently return nil when active, active.scope, or runtime are nil. While this defensive approach prevents panics, it may make debugging harder if hooks are unexpectedly not being dispatched.

Per coding guidelines, consider adding structured logging with log/slog when skipping hook dispatch:

💡 Suggested logging
 func (m *RunManager) dispatchReviewWatchPostRoundHook(
 	active *activeRun,
 	runtime *reviewWatchCoordinatorRuntime,
 	payload reviewWatchPostRoundHookPayload,
 ) error {
 	if active == nil || active.scope == nil || runtime == nil {
+		slog.Debug("skipping review watch post-round hook dispatch", "reason", "nil run context")
 		return nil
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/daemon/review_watch_hooks.go` around lines 239 - 261, In
dispatchReviewWatchPostRoundHook (on RunManager) add structured debug logging
before the early returns so nil-guarded cases are observable: detect when active
is nil, when active.scope is nil, or when runtime is nil and call slog.Logger
(or the project's standard logger) with contextual fields (e.g., runID,
workflowSlug, provider, and which variable was nil) using active.ctx when
available, then return nil as before; ensure you instrument the same function
names seen here (dispatchReviewWatchPostRoundHook, activeRun,
reviewWatchCoordinatorRuntime) and keep the original behavior of calling
model.DispatchObserverHook and waitReviewWatchObserverHooks when not nil.
internal/store/globaldb/sync.go (1)

228-299: 💤 Low value

Potential redundant active run check after atomic conditional delete.

Lines 287-295 re-count active runs after deleteActiveWorkflowIfNoActiveRuns returns deleted=false. However, the DELETE at lines 319-327 is atomic with a NOT EXISTS subquery that already checks for active runs. If the delete didn't happen, the archiveReasonActiveRuns reason is always applied regardless of the actual cause (which could also be archived_at IS NOT NULL).

This may report misleading skip reasons for already-archived workflows.

Consider distinguishing skip reasons
 		deleted, err := g.deleteActiveWorkflowIfNoActiveRuns(ctx, workflow.ID)
 		if err != nil {
 			return WorkflowPruneResult{}, err
 		}
 		if deleted {
 			result.PrunedSlugs = append(result.PrunedSlugs, workflow.Slug)
 			continue
 		}

-		activeRuns, err = g.countActiveRunsForWorkflow(ctx, workflow.ID)
-		if err != nil {
-			return WorkflowPruneResult{}, err
+		// Workflow was not deleted - could be archived or have active runs
+		if workflow.ArchivedAt != nil {
+			result.Skipped = append(result.Skipped, WorkflowPruneSkipped{
+				Slug:   workflow.Slug,
+				Reason: "already archived",
+			})
+		} else {
+			activeRuns, err = g.countActiveRunsForWorkflow(ctx, workflow.ID)
+			if err != nil {
+				return WorkflowPruneResult{}, err
+			}
+			result.Skipped = append(result.Skipped, WorkflowPruneSkipped{
+				Slug:       workflow.Slug,
+				Reason:     archiveReasonActiveRuns,
+				ActiveRuns: activeRuns,
+			})
 		}
-		result.Skipped = append(result.Skipped, WorkflowPruneSkipped{
-			Slug:       workflow.Slug,
-			Reason:     archiveReasonActiveRuns,
-			ActiveRuns: activeRuns,
-		})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/store/globaldb/sync.go` around lines 228 - 299,
PruneMissingActiveWorkflows may mis-report skip reasons because after calling
deleteActiveWorkflowIfNoActiveRuns (which performs an atomic DELETE guarded by
NOT EXISTS active runs and archived_at IS NULL) we re-count active runs and
always set WorkflowPruneSkipped.Reason to archiveReasonActiveRuns; instead,
update the logic in PruneMissingActiveWorkflows to distinguish why delete failed
by (a) checking the workflow's ArchivedAt/archived flag first (using the
workflow record from ListWorkflows or refetching if needed) and set the skip
reason to an "already archived" reason when archived_at != nil, (b) only call
countActiveRunsForWorkflow to set archiveReasonActiveRuns if the workflow is not
archived, and (c) preserve the existing branch where deleted==true appends to
PrunedSlugs; use the identifiers PruneMissingActiveWorkflows,
deleteActiveWorkflowIfNoActiveRuns, countActiveRunsForWorkflow,
WorkflowPruneSkipped, and archiveReasonActiveRuns to locate and implement this
change.
internal/daemon/review_watch.go (1)

1048-1071: 💤 Low value

Simplify redundant reservation check.

Line 1055 checks both runID != "" and keyReserved(...). Since keyReserved already checks if the key exists in the map, and an empty string value is used for reservations (line 1069), the runID != "" check is redundant for the reservation case (though useful for the "already active with run" case).

The logic is correct but slightly confusing. Consider a small refactor for clarity:

-	if runID := strings.TrimSpace(m.activeReviewWatches[key]); runID != "" || keyReserved(m.activeReviewWatches, key) {
+	if existingRunID, reserved := m.activeReviewWatches[key]; reserved {
 		return apicore.NewProblem(
 			http.StatusConflict,
 			"review_watch_already_active",
 			"review watch is already active for this workspace, provider, and PR",
 			map[string]any{
 				"workspace_id": key.WorkspaceID,
 				"provider":     key.Provider,
 				"pr":           key.PR,
-				"run_id":       runID,
+				"run_id":       existingRunID,
 			},
 			nil,
 		)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/daemon/review_watch.go` around lines 1048 - 1071, The check in
reserveReviewWatch is confusing because it tests both runID != "" and
keyReserved(m.activeReviewWatches, key); simplify by using keyReserved to detect
an existing reservation or active entry and separately check runID to detect an
active run: retrieve runID := strings.TrimSpace(m.activeReviewWatches[key])
after normalizing key, then if keyReserved(m.activeReviewWatches, key) return
the conflict with run_id set to runID, else if runID != "" return the conflict
for an active run; update reserveReviewWatch to rely on keyReserved and
activeReviewWatches consistently and keep the allocation line
m.activeReviewWatches[key] = "" for the reservation case.
internal/daemon/run_manager.go (1)

764-798: 💤 Low value

Confusing fallback error handling in rejectCompletedTaskWorkflow.

The control flow at lines 794-798 has subtle behavior: when taskRows is empty, it returns nil (allowing the run), but when taskRows is non-empty, it returns the original SnapshotTaskMeta error. This masking could confuse callers since the returned error references task metadata inspection but the actual check was done via DB rows.

Consider returning a clearer error when falling back to DB succeeds but metadata inspection initially failed:

 	if len(taskRows) == 0 {
 		return nil
 	}
-	return fmt.Errorf("inspect task metadata for %s: %w", strings.TrimSpace(workflowSlug), err)
+	// DB shows tasks exist but metadata snapshot failed - proceed cautiously
+	return nil

Alternatively, if the metadata error should still block execution when tasks exist, document this intent clearly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/daemon/run_manager.go` around lines 764 - 798, When SnapshotTaskMeta
(taskscore.SnapshotTaskMeta) fails and you fall back to
m.globalDB.ListTaskItems, don't mask the original metadata error with a generic
message; instead, after calling ListTaskItems and summarizeTaskRows, if
len(taskRows) == 0 return nil, if counts.Total>0 && counts.Pending==0 return
workflowNoPendingTasksProblem(workflowSlug, counts), and otherwise return a
clearer wrapped error that includes the original SnapshotTaskMeta error plus DB
findings (e.g. include counts.Total and counts.Pending) so callers see both the
metadata inspection failure and what the DB check returned; update the final
return to combine err with DB info rather than returning the original err alone.
internal/daemon/review_watch_git_test.go (1)

192-217: 💤 Low value

Missing t.Parallel() in subtest.

Per coding guidelines, independent subtests should use t.Parallel(). The subtest at line 193 is missing this call.

 	for _, failingCall := range requiredReads {
 		t.Run(failingCall, func(t *testing.T) {
+			t.Parallel()
 			wantErr := errors.New("read failed")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/daemon/review_watch_git_test.go` around lines 192 - 217, Add
t.Parallel() inside the subtest body so each case runs concurrently; inside the
loop capture the loop variable to avoid closure/race (e.g., assign local :=
failingCall or change the t.Run callback to accept the name) and then call
t.Parallel() as the first statement in that subtest. Update the subtest that
invokes t.Run with failingCall (which constructs execReviewWatchGit and calls
git.State) to use the captured local variable when comparing the constructed
command and when naming the subtest.
internal/daemon/review_watch_test.go (1)

629-648: ⚡ Quick win

Mark independent failure-state subtests with t.Parallel().

These table-driven subtests appear isolated (newReviewWatchTestEnv per case), so they should run in parallel for faster feedback and better race-safety coverage.

As per coding guidelines, "Use t.Parallel() for independent subtests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/daemon/review_watch_test.go` around lines 629 - 648, Add
t.Parallel() at the start of each subtest body so the table-driven cases run
concurrently: inside the t.Run callback (where newReviewWatchTestEnv,
startReviewWatch, waitForRun, and requireRunEvent are invoked for each tc) call
t.Parallel() immediately before creating the env and running the test logic to
mark the subtest as parallelizable while preserving isolation per tc.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/api/core/handlers_service_errors_test.go`:
- Around line 303-347: The test case names used as tc.name for t.Run are not
following the required "Should..." pattern; update the three table entries whose
name strings are "review watch active conflict", "review watch invalid request",
and "review watch unavailable" to use descriptive "Should..." phrases (e.g.,
"Should return conflict when review watch already active", "Should return
unprocessable entity for invalid watch request", "Should return service
unavailable when review watch is unavailable") so t.Run(tc.name, ...) uses the
mandated naming convention.

In `@internal/core/agent/client_test.go`:
- Around line 55-88: Wrap the entire body of
TestClientCreateSessionStartsAgentProcessInWorkingDirectory in a t.Run subtest
using a descriptive "Should..." name (e.g., t.Run("Should start agent process in
provided working directory", func(t *testing.T) { ... })) so the test follows
the mandated subtest pattern; keep all existing setup and assertions
(newTestClient, SessionRequest with WorkingDir, client.CreateSession,
collectSessionUpdates, session.Err(), client.Close()) inside that t.Run callback
and preserve t.Parallel() placement per project convention.

In `@internal/core/archive_test.go`:
- Around line 228-264: Refactor the two tests
TestArchiveTaskWorkflowAllowsResolvedReviewOnlyWorkflow and
TestArchiveTaskWorkflowRejectsUnresolvedReviewOnlyWorkflow into a single
table-driven test using t.Run("Should ...") subtests: create a slice of cases
describing name ("Should archive resolved review-only", "Should reject
unresolved review-only"), inputs (use writeArchiveReviewRound to write rounds
and mustSyncArchiveWorkflow), expected error (nil vs
globaldb.ErrWorkflowNotArchivable) and expected result assertions
(WorkflowsScanned/Archived/Skipped and os.Stat checks), then iterate cases and
call t.Run(case.name, func(t *testing.T) { t.Parallel(); /* call
Archive(context.Background(), ArchiveConfig{TasksDir: workflowDir}) and assert
*/ }); ensure each subtest uses t.Parallel() where safe and reference Archive,
ArchiveConfig, writeArchiveReviewRound, and mustSyncArchiveWorkflow to locate
logic.

In `@internal/core/provider/overlay_test.go`:
- Around line 169-192: Wrap the existing test logic inside a t.Run subtest
(e.g., t.Run("Should delegate watch status to target", func(t *testing.T) { ...
})) and move the setup/teardown (ActivateOverlay and its defer restore) and the
rest of the assertions into that inner function so the outer
TestAliasedProviderWatchStatusDelegatesToTarget only contains the t.Run call;
keep the same calls to ActivateOverlay, NewRegistry,
base.Register(&overlayTestProvider{name: "base"}), ResolveRegistry,
registry.Get("ext-review") and FetchWatchStatus to preserve behavior and
assertions.

In `@internal/core/provider/provider_test.go`:
- Around line 23-36: Wrap the existing test body of
TestFetchWatchStatusReturnsUnsupportedError in a t.Run subtest with a
descriptive name (e.g., "Should return unsupported error") and move t.Parallel()
into that subtest; specifically, inside
TestFetchWatchStatusReturnsUnsupportedError call t.Run("Should ...", func(t
*testing.T) { t.Parallel(); /* existing assertions using FetchWatchStatus,
unsupportedWatchProvider{}, WatchStatusRequest{PR: "259"} and checks against
ErrWatchStatusUnsupported and WatchStatusUnsupported */ }) so the test follows
the enforced t.Run("Should...") pattern while still running in parallel.

In `@internal/core/providerdefaults/defaults_test.go`:
- Around line 13-65: Wrap the body of
TestDefaultRegistryForWorkspaceRunsCodeRabbitCommandsFromWorkspaceRoot inside a
t.Run subtest (e.g., t.Run("Should fetch workspace-scoped CodeRabbit comments
from workspace root", func(t *testing.T) { ... })) so the test follows the
required t.Run("Should...") pattern; keep the existing setup (workspaceRoot,
fake gh script, env changes, registry := DefaultRegistryForWorkspace,
registry.Get, FetchReviews and assertions) but move them into the anonymous
subtest function and use that t parameter for assertions and t.Setenv/t.Chdir
calls.

In `@internal/core/workspace/config_validate.go`:
- Around line 189-194: The validation currently only rejects explicit false for
defaults.AutoCommit but lets nil pass; update the conditional that guards the
error in Validate (where cfg.AutoPush and defaults.AutoCommit are checked) to
use the isEnabled helper consistently: replace the check "defaults.AutoCommit !=
nil && !*defaults.AutoCommit" with "!isEnabled(defaults.AutoCommit, false)" so
nil and false are treated the same; keep the existing fmt.Errorf call that uses
configFieldName(scope, "watch_reviews.auto_push") and configFieldName(scope,
"defaults.auto_commit") unchanged.

In `@internal/daemon/extension_bridge.go`:
- Around line 91-93: The code currently replaces a nil context with
context.Background(), which breaks cancellation propagation for daemon-run
paths; instead, in the function that calls
resolveExtensionBridgeWorkspaceRoot(ctx) (in extension_bridge.go, part of the
DaemonHostBridge implementation), detect if ctx == nil and immediately return an
error (e.g., fmt.Errorf or wrapped error) indicating a missing context rather
than substituting context.Background(); keep
resolveExtensionBridgeWorkspaceRoot(ctx) and all downstream calls unchanged so
callers must pass a valid context.

In `@internal/daemon/query_service.go`:
- Around line 159-165: The embedded Workflow summary is built with
transportWorkflowSummary(workflow), which omits derived task_counts and
start-block fields; instead call the counted summary builder (buildWorkflowCard
or the function that accepts both workflow and counts) to construct the summary
using the existing counts variable, then set summary.ArchiveEligible =
&archiveEligible and summary.ArchiveReason = archiveReason before returning the
WorkflowOverviewPayload so the embedded Workflow matches the top-level
TaskCounts payload.

In `@internal/daemon/review_exec_transport_service_test.go`:
- Around line 126-195: These two test functions
(TestTransportReviewServiceFetchSyncsNoMetaRoundAfterLegacyReview and
TestTransportReviewServiceStartWatchUsesDaemonRunManager) must be restructured
into t.Run("Should ...") subtests (or table-driven subtests) to follow the
mandated pattern: wrap each scenario in t.Run with a descriptive "Should ..."
name, move their setup and assertions into the subtest closure, ensure proper
variable capture (use local copies if looping), and call t.Parallel() inside
subtests where safe; keep the existing calls to newTransportReviewService,
service.Fetch/GetRound/ListIssues and service.StartWatch unchanged but invoked
from within the t.Run closures.

In `@internal/daemon/review_watch_test.go`:
- Around line 961-967: In the execute helper function, move the nil-check for
cfg (model.RuntimeConfig) to before calling resolveIssues so you don't
dereference a nil pointer; i.e., first return errors.New("runtime config is
required") when cfg == nil, then call resolveIssues(ctx, preparation, cfg) and
handle its error, referencing the anonymous execute func and the resolveIssues
call to locate the change.
- Around line 726-743: The tests currently only assert that
resolveReviewWatchOptions and resolveReviewWatchDuration return a non-nil error;
change them to assert specific error content/types so they validate the intended
validation branch: for resolveReviewWatchOptions calls (with
apicore.ReviewWatchRequest{PRRef: "123"} and {Provider: "coderabbit"}) assert
the returned error is the expected validation error (use errors.Is or test
helper ErrorAs/ErrorContains with the known validation error sentinel/message),
and for resolveReviewWatchDuration calls assert the error contains the expected
message for zero duration and for invalid parse (use ErrorContains or
strings.Contains on the error string or errors.Is if there’s a sentinel).
Reference the functions resolveReviewWatchOptions and resolveReviewWatchDuration
and the input shapes apicore.ReviewWatchRequest to locate tests to update.
- Around line 26-810: Wrap each top-level test body in this file (e.g.
TestRunManagerReviewWatchCompletesCleanWithoutEmptyRound,
TestRunManagerReviewWatchPersistsRoundAndStartsOneChildRun,
TestRunManagerReviewWatchRejectsDuplicateActiveWatch, etc.) with a t.Run("Should
...", func(t *testing.T) { ... }) subtest using a human-readable "Should ..."
description derived from the test name, moving the current contents inside that
closure; preserve any existing inner t.Run calls (like the table-driven cases in
TestRunManagerReviewWatchFailureStates) and keep all setup/teardown, helper
calls, and assertions unchanged, only adding the t.Run wrapper to satisfy the
required subtest pattern.

In `@internal/daemon/task_transport_service.go`:
- Around line 67-77: The current loop in task_transport_service.go causes N+1 DB
calls by calling s.globalDB.ListTaskItems and attachWorkflowArchiveEligibility
for each workflow row; fix this by fetching task rows and archive-eligibility in
bulk before the loop (e.g., add or use a DB method like
ListTaskItemsForWorkflowIDs(ctx, ids) to get all task rows keyed by workflow ID
and a bulk attachWorkflowArchiveEligibilityBulk(ctx, db, rows) or a single query
that returns archive eligibility per workflow), then iterate rows and call
transportWorkflowSummaryWithTaskCounts(row,
summarizeTaskRows(bulkTaskMap[row.ID])) and set the precomputed eligibility from
the bulk result instead of per-row DB calls.
- Around line 99-103: GetWorkflow builds the summary using
transportWorkflowSummary which omits the derived task-count/start-block fields;
replace that call with transportWorkflowSummaryWithTaskCounts (and keep the
existing attachWorkflowArchiveEligibility call) so the single-workflow path
populates the same derived fields as ListWorkflows; update the code in
task_transport_service.go to create summary via
transportWorkflowSummaryWithTaskCounts(ctx, row, s.globalDB or same params used
by the list path) and then call attachWorkflowArchiveEligibility(ctx,
s.globalDB, row, &summary) before returning.

In `@internal/store/globaldb/archive_test.go`:
- Around line 100-196: The two new tests violate the project's
subtest/table-driven convention—convert
TestGetWorkflowArchiveEligibilityAllowsResolvedReviewOnlyWorkflow and
TestGetWorkflowArchiveEligibilityBlocksUnresolvedReviewOnlyWorkflow into
subtests under a single TestGetWorkflowArchiveEligibility function that uses
t.Run("Should ...") for each case; preserve t.Parallel(), drive cases via a
table/slice (each case including name, workflowID, review rounds, and expected
counts/archivable/skipReason), and inside each subtest call
mustReconcileArchiveWorkflow, db.GetWorkflowArchiveEligibility, and assert
TaskTotal/ReviewIssueTotal/UnresolvedReviewIssues/Archivable()/SkipReason()
accordingly so the unique helpers (mustReconcileArchiveWorkflow, mustWorkspace,
openTestGlobalDB, GetWorkflowArchiveEligibility) remain referenced and tests
follow the required pattern.

In `@packages/ui/src/components/surface-card.tsx`:
- Line 26: The header/footer class forces shrink-0 on the last child which, when
there's only one child (it is both first and last), prevents shrinking; update
the selector so shrink-0 is only applied when the last child is not the only
child—replace the [&>*:last-child]:shrink-0 usage with a non-only-child selector
such as [&>*:last-child:not(:only-child)]:shrink-0 (and apply the same change
for the other occurrence referenced in the file) while keeping the existing
[&>*:first-child]:min-w-0 rule.

In `@pkg/compozy/events/event_test.go`:
- Line 397: Rename the table-driven test case's name field to follow the
required t.Run("Should...") pattern: change the case currently named "review
watch lifecycle" to a name starting with "Should", e.g. "Should review watch
lifecycle", so the test invocation t.Run(...) conforms to the enforced naming
convention; update the entry in the test cases slice in event_test.go where the
name key is defined (the test case that currently reads "review watch
lifecycle").

In `@pkg/compozy/events/kinds/payload_compat_test.go`:
- Around line 91-139: Wrap the existing assertions in
TestReviewWatchPayloadJSONCompatibility inside a t.Run subtest with a
descriptive "Should ..." name (e.g., t.Run("Should serialize ReviewWatchPayload
to stable JSON map", func(t *testing.T) { ... })), move the t.Parallel() call
into the subtest body (call t.Parallel() as the first line inside the func), and
keep all existing setup, the payload variable, mustMarshalMap(t, payload) call,
and the reflect.DeepEqual check unchanged inside that subtest so the test logic
and assertions remain identical.

In `@web/src/systems/reviews/hooks/use-reviews.ts`:
- Around line 46-52: The enabled check currently allows round == 0 which causes
invalid API calls; update the guard to require round > 0 and add a runtime
validation before calling getReviewRound to throw on non-positive rounds.
Specifically, change the react-query enabled expression to use round != null &&
round > 0 (instead of round >= 0) and adjust the early validation (the if (round
== null) block) to also reject round <= 0 (e.g., if (round == null || round <=
0) throw ...) so getReviewRound is never invoked with round 0.

---

Nitpick comments:
In `@internal/core/reviews/store.go`:
- Around line 211-214: The call to roundMetaFromIssueFrontMatter returns raw err
which is returned directly; wrap this error with call-site context using
fmt.Errorf to preserve `%w` semantics so upstream matching still works. Update
the error return in the block that calls roundMetaFromIssueFrontMatter (the
variables meta, err) to return a wrapped error like fmt.Errorf("extracting round
meta from issue front matter: %w", err), ensuring you import fmt if not already
imported.

In `@internal/core/sync_test.go`:
- Around line 573-600: Rename the subtest label to follow the "Should..."
convention: in the t.Run call inside the Test (the subtest that writes files and
calls collectReviewRounds), change the name string from "consistent provider and
pr" to something like "Should project metadata when provider and pr are
consistent" so the test description follows the project's naming guideline while
leaving the logic (writeSyncWorkflowFile calls, collectReviewRounds, and
assertions on rounds[0].Provider, rounds[0].PRRef, ResolvedCount,
UnresolvedCount) unchanged.

In `@internal/daemon/review_watch_git_test.go`:
- Around line 192-217: Add t.Parallel() inside the subtest body so each case
runs concurrently; inside the loop capture the loop variable to avoid
closure/race (e.g., assign local := failingCall or change the t.Run callback to
accept the name) and then call t.Parallel() as the first statement in that
subtest. Update the subtest that invokes t.Run with failingCall (which
constructs execReviewWatchGit and calls git.State) to use the captured local
variable when comparing the constructed command and when naming the subtest.

In `@internal/daemon/review_watch_hooks.go`:
- Around line 239-261: In dispatchReviewWatchPostRoundHook (on RunManager) add
structured debug logging before the early returns so nil-guarded cases are
observable: detect when active is nil, when active.scope is nil, or when runtime
is nil and call slog.Logger (or the project's standard logger) with contextual
fields (e.g., runID, workflowSlug, provider, and which variable was nil) using
active.ctx when available, then return nil as before; ensure you instrument the
same function names seen here (dispatchReviewWatchPostRoundHook, activeRun,
reviewWatchCoordinatorRuntime) and keep the original behavior of calling
model.DispatchObserverHook and waitReviewWatchObserverHooks when not nil.

In `@internal/daemon/review_watch_test.go`:
- Around line 629-648: Add t.Parallel() at the start of each subtest body so the
table-driven cases run concurrently: inside the t.Run callback (where
newReviewWatchTestEnv, startReviewWatch, waitForRun, and requireRunEvent are
invoked for each tc) call t.Parallel() immediately before creating the env and
running the test logic to mark the subtest as parallelizable while preserving
isolation per tc.

In `@internal/daemon/review_watch.go`:
- Around line 1048-1071: The check in reserveReviewWatch is confusing because it
tests both runID != "" and keyReserved(m.activeReviewWatches, key); simplify by
using keyReserved to detect an existing reservation or active entry and
separately check runID to detect an active run: retrieve runID :=
strings.TrimSpace(m.activeReviewWatches[key]) after normalizing key, then if
keyReserved(m.activeReviewWatches, key) return the conflict with run_id set to
runID, else if runID != "" return the conflict for an active run; update
reserveReviewWatch to rely on keyReserved and activeReviewWatches consistently
and keep the allocation line m.activeReviewWatches[key] = "" for the reservation
case.

In `@internal/daemon/run_manager.go`:
- Around line 764-798: When SnapshotTaskMeta (taskscore.SnapshotTaskMeta) fails
and you fall back to m.globalDB.ListTaskItems, don't mask the original metadata
error with a generic message; instead, after calling ListTaskItems and
summarizeTaskRows, if len(taskRows) == 0 return nil, if counts.Total>0 &&
counts.Pending==0 return workflowNoPendingTasksProblem(workflowSlug, counts),
and otherwise return a clearer wrapped error that includes the original
SnapshotTaskMeta error plus DB findings (e.g. include counts.Total and
counts.Pending) so callers see both the metadata inspection failure and what the
DB check returned; update the final return to combine err with DB info rather
than returning the original err alone.

In `@internal/store/globaldb/sync.go`:
- Around line 228-299: PruneMissingActiveWorkflows may mis-report skip reasons
because after calling deleteActiveWorkflowIfNoActiveRuns (which performs an
atomic DELETE guarded by NOT EXISTS active runs and archived_at IS NULL) we
re-count active runs and always set WorkflowPruneSkipped.Reason to
archiveReasonActiveRuns; instead, update the logic in
PruneMissingActiveWorkflows to distinguish why delete failed by (a) checking the
workflow's ArchivedAt/archived flag first (using the workflow record from
ListWorkflows or refetching if needed) and set the skip reason to an "already
archived" reason when archived_at != nil, (b) only call
countActiveRunsForWorkflow to set archiveReasonActiveRuns if the workflow is not
archived, and (c) preserve the existing branch where deleted==true appends to
PrunedSlugs; use the identifiers PruneMissingActiveWorkflows,
deleteActiveWorkflowIfNoActiveRuns, countActiveRunsForWorkflow,
WorkflowPruneSkipped, and archiveReasonActiveRuns to locate and implement this
change.

In `@sdk/extension/internal_test.go`:
- Around line 241-242: Rename the subtest name used in the t.Run call from the
raw hook value to the required "Should..." pattern: replace
t.Run(string(tc.hook), ...) with a name that prefixes the hook with "Should "
(e.g., "Should "+string(tc.hook) or fmt.Sprintf("Should %s", tc.hook)); update
the t.Run invocation that currently wraps t.Parallel() so all subtests follow
the "Should..." naming convention (adjust imports if you choose fmt.Sprintf).

In `@web/src/systems/reviews/components/review-detail-view.tsx`:
- Around line 286-294: The displayReviewTitle function currently removes all
underscores via the .replace(/_/g, "") call which can mangle snake_case
identifiers; update displayReviewTitle to replace underscores with spaces (e.g.,
replace /_/g with " " or a regex that collapses consecutive underscores to a
single space), then collapse multiple whitespace (.replace(/\s+/g, " ")) and
trim as already done so titles like "Fix my_variable_name" become "Fix my
variable name" while preserving spacing and readability.

In `@web/src/systems/reviews/components/review-round-detail-view.test.tsx`:
- Around line 99-108: The test currently calls renderRoundDetail multiple times
causing cumulative DOM; update the test to reuse a single render tree by either
calling cleanup() between renderRoundDetail invocations or by using the rerender
function returned from the initial renderRoundDetail call to update props/state;
modify the "Should render loading, error, empty, and refreshing states" test to
call renderRoundDetail once, then use rerender(...) to switch to { issues: [],
issuesError: "boom" } and then to { issues: [] }, or call cleanup() before each
subsequent renderRoundDetail, ensuring assertions (screen.getByTestId(...)) run
against a single, non-leaking render tree.

In `@web/src/systems/runs/components/runs-list-view.test.tsx`:
- Around line 141-144: Add an assertion to the test that verifies the status
badge does not shrink: locate the status element retrieved by
getByTestId(`runs-list-status-${longRunId}`) in the runs-list-view test and add
an expectation that it includes the "shrink-0" class (e.g.,
expect(screen.getByTestId(`runs-list-status-${longRunId}`)).toHaveClass("shrink-0")).
This will complement the existing truncation and text assertions and guard the
non-shrinking layout behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8b396c88-d3ed-469b-a759-9bb1877793af

📥 Commits

Reviewing files that changed from the base of the PR and between d49ecf3 and 3e6f65e.

⛔ Files ignored due to path filters (9)
  • .codex/ledger/2026-04-30-MEMORY-sync-prune-archive.md is excluded by !**/*.md
  • .codex/ledger/2026-04-30-MEMORY-sync-review-rounds.md is excluded by !**/*.md
  • README.md is excluded by !**/*.md
  • docs/events.md is excluded by !**/*.md
  • docs/extensibility/capability-reference.md is excluded by !**/*.md
  • docs/extensibility/hook-reference.md is excluded by !**/*.md
  • openapi/compozy-daemon.json is excluded by !**/*.json
  • skills/compozy/references/config-reference.md is excluded by !**/*.md
  • web/src/generated/compozy-openapi.d.ts is excluded by !**/generated/**
📒 Files selected for processing (127)
  • internal/api/client/reviews_exec.go
  • internal/api/client/reviews_exec_test.go
  • internal/api/contract/routes.go
  • internal/api/contract/types.go
  • internal/api/core/handlers.go
  • internal/api/core/handlers_contract_test.go
  • internal/api/core/handlers_service_errors_test.go
  • internal/api/core/handlers_smoke_test.go
  • internal/api/core/interfaces.go
  • internal/api/core/routes.go
  • internal/api/httpapi/openapi_contract_test.go
  • internal/api/httpapi/transport_integration_test.go
  • internal/cli/commands_simple.go
  • internal/cli/daemon_commands.go
  • internal/cli/daemon_commands_test.go
  • internal/cli/daemon_exec_test_helpers_test.go
  • internal/cli/reviews_exec_daemon.go
  • internal/cli/reviews_exec_daemon_additional_test.go
  • internal/cli/root.go
  • internal/cli/skills_preflight.go
  • internal/cli/state.go
  • internal/cli/workspace_config.go
  • internal/core/agent/client.go
  • internal/core/agent/client_test.go
  • internal/core/archive_test.go
  • internal/core/extension/manifest.go
  • internal/core/fetch.go
  • internal/core/fetch_test.go
  • internal/core/model/workflow_ops.go
  • internal/core/provider/coderabbit/coderabbit.go
  • internal/core/provider/coderabbit/coderabbit_test.go
  • internal/core/provider/coderabbit/nitpicks.go
  • internal/core/provider/overlay.go
  • internal/core/provider/overlay_test.go
  • internal/core/provider/provider.go
  • internal/core/provider/provider_test.go
  • internal/core/providerdefaults/defaults.go
  • internal/core/providerdefaults/defaults_test.go
  • internal/core/reviews/store.go
  • internal/core/reviews/store_test.go
  • internal/core/run/executor/execution_test.go
  • internal/core/run/executor/review_hooks.go
  • internal/core/sync.go
  • internal/core/sync_test.go
  • internal/core/workspace/config_merge.go
  • internal/core/workspace/config_test.go
  • internal/core/workspace/config_types.go
  • internal/core/workspace/config_validate.go
  • internal/daemon/extension_bridge.go
  • internal/daemon/query_service.go
  • internal/daemon/query_service_test.go
  • internal/daemon/review_exec_transport_service.go
  • internal/daemon/review_exec_transport_service_test.go
  • internal/daemon/review_watch.go
  • internal/daemon/review_watch_git.go
  • internal/daemon/review_watch_git_test.go
  • internal/daemon/review_watch_hooks.go
  • internal/daemon/review_watch_test.go
  • internal/daemon/run_manager.go
  • internal/daemon/run_manager_test.go
  • internal/daemon/task_transport_service.go
  • internal/daemon/transport_mappers.go
  • internal/daemon/transport_service_test.go
  • internal/daemon/workspace_refresh.go
  • internal/store/globaldb/archive.go
  • internal/store/globaldb/archive_test.go
  • internal/store/globaldb/sync.go
  • internal/store/globaldb/sync_test.go
  • packages/ui/src/components/section-heading.tsx
  • packages/ui/src/components/surface-card.tsx
  • pkg/compozy/events/docs_test.go
  • pkg/compozy/events/event.go
  • pkg/compozy/events/event_test.go
  • pkg/compozy/events/kinds/payload_compat_test.go
  • pkg/compozy/events/kinds/review.go
  • sdk/extension-sdk-ts/src/extension.ts
  • sdk/extension-sdk-ts/src/handlers.ts
  • sdk/extension-sdk-ts/src/types.ts
  • sdk/extension-sdk-ts/test/extension.test.ts
  • sdk/extension-sdk-ts/test/handlers.test.ts
  • sdk/extension/extension.go
  • sdk/extension/extension_test.go
  • sdk/extension/handlers.go
  • sdk/extension/hooks.go
  • sdk/extension/internal_test.go
  • sdk/extension/smoke_test.go
  • sdk/extension/types.go
  • test/frontend-verification-contract.test.ts
  • web/e2e/daemon-ui.smoke.spec.ts
  • web/e2e/global.setup.ts
  • web/e2e/support/daemon-fixture.ts
  • web/src/lib/api-client.test.ts
  • web/src/lib/api-client.ts
  • web/src/routes/-reviews-flow.integration.test.tsx
  • web/src/routes/_app/index.tsx
  • web/src/routes/_app/reviews.tsx
  • web/src/routes/_app/reviews_.$slug.$round.tsx
  • web/src/routes/_app/reviews_.$slug.$round_.$issueId.tsx
  • web/src/routes/_app/stories/-reviews.$slug.$round.stories.tsx
  • web/src/routes/_app/stories/-reviews.stories.tsx
  • web/src/routes/_app/workflows.tsx
  • web/src/storybook/route-stories.test.tsx
  • web/src/systems/dashboard/components/dashboard-view.test.tsx
  • web/src/systems/dashboard/components/dashboard-view.tsx
  • web/src/systems/reviews/adapters/reviews-api.test.ts
  • web/src/systems/reviews/adapters/reviews-api.ts
  • web/src/systems/reviews/components/review-detail-view.test.tsx
  • web/src/systems/reviews/components/review-detail-view.tsx
  • web/src/systems/reviews/components/review-round-detail-view.test.tsx
  • web/src/systems/reviews/components/review-round-detail-view.tsx
  • web/src/systems/reviews/components/reviews-index-view.test.tsx
  • web/src/systems/reviews/components/reviews-index-view.tsx
  • web/src/systems/reviews/hooks/use-reviews.ts
  • web/src/systems/reviews/index.ts
  • web/src/systems/reviews/lib/query-keys.ts
  • web/src/systems/reviews/mocks/fixtures.ts
  • web/src/systems/reviews/mocks/handlers.ts
  • web/src/systems/reviews/mocks/index.ts
  • web/src/systems/runs/components/runs-list-view.test.tsx
  • web/src/systems/runs/components/runs-list-view.tsx
  • web/src/systems/workflows/components/task-board-view.test.tsx
  • web/src/systems/workflows/components/task-board-view.tsx
  • web/src/systems/workflows/components/workflow-inventory-view.test.tsx
  • web/src/systems/workflows/components/workflow-inventory-view.tsx
  • web/src/systems/workflows/index.ts
  • web/src/systems/workflows/lib/sync-summary.test.ts
  • web/src/systems/workflows/lib/sync-summary.ts

Comment thread internal/api/core/handlers_service_errors_test.go
Comment thread internal/core/agent/client_test.go
Comment thread internal/core/archive_test.go Outdated
Comment thread internal/core/provider/overlay_test.go
Comment thread internal/core/provider/provider_test.go
Comment thread internal/store/globaldb/archive_test.go Outdated
Comment thread packages/ui/src/components/surface-card.tsx Outdated
Comment thread pkg/compozy/events/event_test.go Outdated
Comment thread pkg/compozy/events/kinds/payload_compat_test.go
Comment thread web/src/systems/reviews/hooks/use-reviews.ts Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (2)
internal/core/agent/client_test.go (1)

67-88: ⚡ Quick win

Ensure client teardown always runs, even on early assertion failures.

If an assertion fails before Line 86, client.Close() is skipped and the helper process can leak into other parallel tests. Register cleanup right after client creation.

Suggested change
 		client := newTestClient(t, scenario)
+		t.Cleanup(func() {
+			if err := client.Close(); err != nil {
+				t.Fatalf("close client: %v", err)
+			}
+		})
 		session, err := client.CreateSession(context.Background(), SessionRequest{
 			WorkingDir: workingDir,
 			Prompt:     []byte(scenario.ExpectedPrompt),
 		})
@@
-		if err := client.Close(); err != nil {
-			t.Fatalf("close client: %v", err)
-		}
 	})
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/core/agent/client_test.go` around lines 67 - 88, The test currently
creates the client with newTestClient(t, scenario) but calls client.Close() only
at the end, so if an assertion fails earlier the client (and helper process) can
leak; immediately register teardown by calling t.Cleanup(func(){ _ =
client.Close() }) right after creating the client to ensure client.Close() runs
on test exit regardless of failures (reference newTestClient and client.Close in
the session test).
internal/core/reviews/store_test.go (1)

256-271: ⚡ Quick win

Add a populated pr scenario to this table

This table now validates only empty/missing pr. Add a case like pr: "259" with wantPR: "259" to guard against regressions where non-empty PR is dropped during refresh/persist.

As per coding guidelines, "MUST test meaningful business logic, not trivial operations."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/core/reviews/store_test.go` around lines 256 - 271, Add a test case
to the existing cases slice in the test (the table-driven test that defines
name, prLine, wantPR, createdAt) that covers a populated PR value: e.g. add an
entry with name "populated pr field", prLine `pr: "259"`, wantPR `"259"`, and
the same createdAt value used by the other cases (time.Date(2026, 3, 28, 10, 0,
0, 0, time.UTC)). Ensure this new case is included in the loop that exercises
the refresh/persist logic so the assertion compares the persisted/read PR
against wantPR.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/cli/reviews_exec_daemon_additional_test.go`:
- Around line 498-584: Rename and wrap the standalone test function
TestReviewsWatchCommandBuildsDaemonRequest and all new watch subtests into the
t.Run("Should ...") pattern required by our guidelines: for
TestReviewsWatchCommandBuildsDaemonRequest, wrap its body in t.Run("Should build
daemon request for watch command", func(t *testing.T) { ... }) and similarly
rename and wrap each new subtest (those currently added after the
TestReviewsWatchCommandBuildsDaemonRequest and any subtests referenced between
the old ranges) to use descriptive "Should ..." titles; ensure you keep the
existing assertions and variables (e.g., client.startWatchReq, req, overrides,
batching) unchanged, only move them inside the new t.Run closures and update
subtest names to the "Should..." prefix consistently across all watch-related
tests.

In `@internal/core/reviews/store_test.go`:
- Around line 263-269: Update the two table-driven test case "name" values that
currently read "empty pr field" and "missing pr field" to follow the required
t.Run("Should...") naming convention (e.g., "ShouldAllowEmptyPRField" and
"ShouldAllowMissingPRField") so that the test cases in the slice (the entries
with name: "empty pr field" and name: "missing pr field" in
internal/core/reviews/store_test.go) use the Must-use t.Run("Should...")
pattern.
- Around line 385-406: Convert the test helper reviewIssueContentWithRound to
accept a *testing.T parameter and call t.Helper() at the top of the function so
failures point to the caller; update any test callers that invoke
reviewIssueContentWithRound to pass their *testing.T. Specifically, change the
signature of reviewIssueContentWithRound to include t *testing.T, add t.Helper()
as the first statement inside the function, and update all usages in the tests
to pass the test instance.

In `@internal/core/sync_test.go`:
- Around line 684-686: Replace the broad nil-check on the error returned by
collectReviewRounds with a specific assertion that the error contains a stable
fragment describing the failure (e.g., "invalid review issue"); update the test
so after calling collectReviewRounds(workflowDir) you assert err != nil and that
strings.Contains(err.Error(), "invalid review issue") (or use your test helper
ErrorContains/require.ErrorContains helper) so the test validates the failure
reason rather than just presence of an error.

In `@internal/daemon/review_watch_git_test.go`:
- Around line 86-88: Replace the generic non-nil assertion for the git.Push
failure with a specific error assertion that verifies the expected failure
reason (e.g., use require.ErrorContains or require.ErrorAs / errors.Is to assert
the "missing target" validation message or the concrete error type returned by
git.Push); update the assertion around the git.Push(context.Background(),
"/repo", "", "feature") call to check the specific error text/type and apply the
same tightening to the other similar assertions in this test file (the blocks
that currently only assert err != nil) so tests fail if a different error path
is taken.
- Around line 109-119: The test calls the system git binary via
newExecReviewWatchGit() and git.run(...) which can fail in minimal CI; add a
guard at the start of the test to detect whether "git" exists on PATH (e.g.
using exec.LookPath or equivalent) and call t.Skipf(...) if not found, then
proceed with the existing assertions; ensure the check runs before creating
newExecReviewWatchGit() and invoking git.run so the test is skipped rather than
failing when git is unavailable.
- Around line 11-239: The tests in this file (e.g.,
TestReviewWatchGitStateReadsOnlyRepositoryState,
TestReviewWatchGitPushUsesOnlyAllowedCommandShape,
TestReviewWatchGitPushRejectsMissingTarget,
TestReviewWatchGitPushWrapsCommandFailure,
TestReviewWatchGitCommandRunnerAndParsers,
TestReviewWatchGitStateWithoutUpstreamStillReportsHead,
TestReviewWatchGitStateValidatesRunnerWorkspaceAndRequiredReads,
TestReviewWatchGitPushValidatesRunnerAndWorkspace) must follow the repository
pattern of using t.Run("Should...") for each case; wrap each test body inside a
top-level t.Run call with a descriptive "Should ..." name (keeping t.Parallel()
inside the subtest where needed) or convert the test to call t.Run(...)
immediately after t.Parallel(), ensuring existing assertions and helper setup
(like the execReviewWatchGit run closures and subtests in
TestReviewWatchGitStateValidatesRunnerWorkspaceAndRequiredReads) remain
unchanged except for being enclosed in the t.Run subtest closure so the suite
consistently uses the t.Run("Should...") pattern.

In `@internal/store/globaldb/read_queries_test.go`:
- Around line 235-240: The test is asserting zero counts for
resolved.Workflow.ID but doesn't verify the map actually contains that key, so a
missing entry would falsely pass; update the assertion in read_queries_test.go
to first check presence (e.g., using the comma-ok pattern: _, ok :=
counts[resolved.Workflow.ID]) and fail if !ok, then proceed to validate
counts[resolved.Workflow.ID].Total/Completed/Pending are all zero to ensure the
key exists and values are correct.
- Around line 181-265: Wrap the TestReadQueriesBulkWorkflowSummariesByID test
body in a t.Run subtest using the "Should..." naming pattern (e.g. t.Run("Should
return correct workflow summary eligibility and counts", func(t *testing.T) {
... })); move the existing t.Parallel() and all setup/assertions into that
subtest closure so the test follows the repo's subtest convention while
preserving behavior of functions like openTestGlobalDB, mustWorkspace,
mustReconcileArchiveWorkflow, TaskCountsByWorkflowIDs, and
WorkflowArchiveEligibilityByIDs.

In `@internal/store/globaldb/sync.go`:
- Around line 251-254: Wrap the error returned by g.ListWorkflows with pruning
context before returning so callers retain operation information; replace the
current bare return of err in the WorkflowPruneResult path (call to
g.ListWorkflows with ListWorkflowsOptions{WorkspaceID: trimmedWorkspaceID}) with
a wrapped error using fmt.Errorf, e.g. include text like "failed to list
workflows for prune (workspace=<trimmedWorkspaceID>): %w" to preserve the
original error.

---

Nitpick comments:
In `@internal/core/agent/client_test.go`:
- Around line 67-88: The test currently creates the client with newTestClient(t,
scenario) but calls client.Close() only at the end, so if an assertion fails
earlier the client (and helper process) can leak; immediately register teardown
by calling t.Cleanup(func(){ _ = client.Close() }) right after creating the
client to ensure client.Close() runs on test exit regardless of failures
(reference newTestClient and client.Close in the session test).

In `@internal/core/reviews/store_test.go`:
- Around line 256-271: Add a test case to the existing cases slice in the test
(the table-driven test that defines name, prLine, wantPR, createdAt) that covers
a populated PR value: e.g. add an entry with name "populated pr field", prLine
`pr: "259"`, wantPR `"259"`, and the same createdAt value used by the other
cases (time.Date(2026, 3, 28, 10, 0, 0, 0, time.UTC)). Ensure this new case is
included in the loop that exercises the refresh/persist logic so the assertion
compares the persisted/read PR against wantPR.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: abf04649-d34f-44c2-b062-131adf165e02

📥 Commits

Reviewing files that changed from the base of the PR and between 3e6f65e and acdd696.

⛔ Files ignored due to path filters (32)
  • .compozy/tasks/reviews-watch/reviews-001/issue_001.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_002.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_003.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_004.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_005.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_006.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_007.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_008.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_009.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_010.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_011.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_012.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_013.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_014.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_015.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_016.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_017.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_018.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_019.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_020.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_021.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_022.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_023.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_024.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_025.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_026.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_027.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_028.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_029.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_030.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_031.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-001/issue_032.md is excluded by !**/*.md
📒 Files selected for processing (40)
  • internal/api/core/handlers_service_errors_test.go
  • internal/cli/reviews_exec_daemon_additional_test.go
  • internal/core/agent/client_test.go
  • internal/core/archive_test.go
  • internal/core/provider/overlay_test.go
  • internal/core/provider/provider_test.go
  • internal/core/providerdefaults/defaults_test.go
  • internal/core/reviews/store.go
  • internal/core/reviews/store_test.go
  • internal/core/sync_test.go
  • internal/core/workspace/config_test.go
  • internal/core/workspace/config_validate.go
  • internal/daemon/extension_bridge.go
  • internal/daemon/query_service.go
  • internal/daemon/query_service_test.go
  • internal/daemon/review_exec_transport_service_test.go
  • internal/daemon/review_watch_git_test.go
  • internal/daemon/review_watch_test.go
  • internal/daemon/run_manager_test.go
  • internal/daemon/task_transport_service.go
  • internal/daemon/transport_read_models_test.go
  • internal/daemon/transport_service_test.go
  • internal/store/globaldb/archive.go
  • internal/store/globaldb/archive_test.go
  • internal/store/globaldb/read_queries.go
  • internal/store/globaldb/read_queries_test.go
  • internal/store/globaldb/sync.go
  • internal/store/globaldb/sync_test.go
  • packages/ui/src/components/surface-card.tsx
  • packages/ui/tests/primitives.test.tsx
  • pkg/compozy/events/event_test.go
  • pkg/compozy/events/kinds/payload_compat_test.go
  • sdk/extension/internal_test.go
  • sdk/extension/smoke_test.go
  • web/src/systems/reviews/components/review-detail-view.test.tsx
  • web/src/systems/reviews/components/review-detail-view.tsx
  • web/src/systems/reviews/components/review-round-detail-view.test.tsx
  • web/src/systems/reviews/hooks/use-reviews.test.tsx
  • web/src/systems/reviews/hooks/use-reviews.ts
  • web/src/systems/runs/components/runs-list-view.test.tsx
✅ Files skipped from review due to trivial changes (3)
  • pkg/compozy/events/event_test.go
  • packages/ui/src/components/surface-card.tsx
  • sdk/extension/internal_test.go
🚧 Files skipped from review as they are similar to previous changes (11)
  • internal/core/provider/overlay_test.go
  • internal/core/provider/provider_test.go
  • internal/core/reviews/store.go
  • internal/core/providerdefaults/defaults_test.go
  • internal/daemon/query_service_test.go
  • internal/core/workspace/config_test.go
  • internal/core/archive_test.go
  • internal/store/globaldb/archive.go
  • web/src/systems/reviews/components/review-detail-view.test.tsx
  • internal/store/globaldb/sync_test.go
  • internal/daemon/transport_service_test.go

Comment thread internal/cli/reviews_exec_daemon_additional_test.go
Comment thread internal/core/reviews/store_test.go Outdated
Comment thread internal/core/reviews/store_test.go
Comment thread internal/core/sync_test.go Outdated
Comment thread internal/daemon/review_watch_git_test.go
Comment thread internal/daemon/review_watch_git_test.go Outdated
Comment thread internal/daemon/review_watch_git_test.go Outdated
Comment thread internal/store/globaldb/read_queries_test.go
Comment thread internal/store/globaldb/read_queries_test.go Outdated
Comment thread internal/store/globaldb/sync.go
coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
internal/core/reviews/store_test.go (2)

25-42: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Should... subtest names in this table as well.

Line 25 ("empty pr field") and Line 42 ("missing pr field") should follow the required Should... naming pattern for t.Run cases.

As per coding guidelines, "MUST use t.Run("Should...") pattern for ALL test cases."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/core/reviews/store_test.go` around lines 25 - 42, Rename the test
case names in the table entries so they follow the required t.Run("Should...")
pattern: change the `name` values currently set to "empty pr field" and "missing
pr field" to descriptive Should... phrases (for example `Should handle empty pr
field` and `Should handle missing pr field`) so that t.Run uses the mandated
naming convention; update the `name` fields in the table where these entries
appear to the new Should... strings (the test table entries with `name:` keys).

391-412: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Mark reviewIssueContentWithRound as a test helper with t.Helper().

This helper should accept t *testing.T and call t.Helper() so failures point to the caller test.

As per coding guidelines, "Mark test helper functions with t.Helper() so stack traces point to the caller."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/core/reviews/store_test.go` around lines 391 - 412, Change the
helper function reviewIssueContentWithRound to accept t *testing.T and call
t.Helper() at the start so failures point to the caller; update the signature
reviewIssueContentWithRound(t *testing.T, status string, prLine string,
createdAt time.Time) and add t.Helper() as the first statement inside the
function, then update all call sites of reviewIssueContentWithRound(...) to pass
the test value (t) as the first argument.
🧹 Nitpick comments (1)
internal/core/sync_test.go (1)

165-181: ⚡ Quick win

Also assert review_issues cleanup in the pruning test.

This test already verifies workflows, task_items, and review_rounds deletion. Adding a review_issues count assertion would close the loop on orphaned review data after pruning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/core/sync_test.go` around lines 165 - 181, Add an assertion that
verifies review_issues rows tied to the pruned beta workflow are deleted: call
queryCount(t, sqlDB, "SELECT COUNT(1) FROM review_issues WHERE workflow_id = ?",
betaWorkflowID) and fail the test if the result is not 0 (similar to the
existing checks for task_items and review_rounds). Place this check alongside
the other post-prune assertions so it uses the same sqlDB, t, and betaWorkflowID
variables.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/core/sync_test.go`:
- Around line 115-182: The test function
TestSyncTaskMetadataRootScanPrunesDeletedWorkflowRows and the other newly added
single-scenario tests must be converted into subtests that follow the repo
pattern by wrapping their assertions in t.Run("Should <describe behavior>",
func(t *testing.T) { ... }), so edit the body of
TestSyncTaskMetadataRootScanPrunesDeletedWorkflowRows (and the other affected
tests at the noted ranges) to move setup/assertion code into a single t.Run call
with a "Should..." description, preserving the existing calls to Sync, sqlDB
queries, and helper funcs (writeSyncWorkflowFile, openSyncSQLite, queryCount)
and returning the same failures using t.Fatalf inside the subtest. Ensure the
top-level Test* function remains and only contains one t.Run subtest per
scenario with the original test logic.

---

Duplicate comments:
In `@internal/core/reviews/store_test.go`:
- Around line 25-42: Rename the test case names in the table entries so they
follow the required t.Run("Should...") pattern: change the `name` values
currently set to "empty pr field" and "missing pr field" to descriptive
Should... phrases (for example `Should handle empty pr field` and `Should handle
missing pr field`) so that t.Run uses the mandated naming convention; update the
`name` fields in the table where these entries appear to the new Should...
strings (the test table entries with `name:` keys).
- Around line 391-412: Change the helper function reviewIssueContentWithRound to
accept t *testing.T and call t.Helper() at the start so failures point to the
caller; update the signature reviewIssueContentWithRound(t *testing.T, status
string, prLine string, createdAt time.Time) and add t.Helper() as the first
statement inside the function, then update all call sites of
reviewIssueContentWithRound(...) to pass the test value (t) as the first
argument.

---

Nitpick comments:
In `@internal/core/sync_test.go`:
- Around line 165-181: Add an assertion that verifies review_issues rows tied to
the pruned beta workflow are deleted: call queryCount(t, sqlDB, "SELECT COUNT(1)
FROM review_issues WHERE workflow_id = ?", betaWorkflowID) and fail the test if
the result is not 0 (similar to the existing checks for task_items and
review_rounds). Place this check alongside the other post-prune assertions so it
uses the same sqlDB, t, and betaWorkflowID variables.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 35ffce55-2011-46c7-8d26-75fcc226c97e

📥 Commits

Reviewing files that changed from the base of the PR and between acdd696 and c051a8c.

⛔ Files ignored due to path filters (12)
  • .compozy/tasks/reviews-watch/reviews-003/issue_001.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_002.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_003.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_004.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_005.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_006.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_007.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_008.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_009.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_010.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_011.md is excluded by !**/*.md
  • .compozy/tasks/reviews-watch/reviews-003/issue_012.md is excluded by !**/*.md
📒 Files selected for processing (7)
  • internal/cli/reviews_exec_daemon_additional_test.go
  • internal/core/agent/client_test.go
  • internal/core/reviews/store_test.go
  • internal/core/sync_test.go
  • internal/daemon/review_watch_git_test.go
  • internal/store/globaldb/read_queries_test.go
  • internal/store/globaldb/sync.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/core/agent/client_test.go
  • internal/daemon/review_watch_git_test.go
  • internal/store/globaldb/sync.go

Comment thread internal/core/sync_test.go
@pedronauck
pedronauck merged commit 752d4b8 into main Apr 30, 2026
7 checks passed
@pedronauck
pedronauck deleted the pn/review-watch branch April 30, 2026 23:44
@coderabbitai coderabbitai Bot mentioned this pull request Jun 11, 2026
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