Atlas recap

2026-09-30

Agent reviews: why they break, and the fix

When an autonomous agent asks for review, people cannot see the changes, cannot find the request, and a merge leaves the branch open. Three separate gaps cause this. This recap explains each one in plain words and proposes one fix per gap. No code has changed yet.

Scope Investigation + proposal, FIMO-2706 Code at develop 8159c88ae Depth Normal Evidence Source reading + read-only prod and staging queries Proposal

Proposal

FIMO-2706 · Agent review flow

What a person should see when an agent asks for review, what they see today, why, and what to change.

Outcome

The review flow is broken in three places, and each needs its own fix. First, the run page shows no changes, because the agent's "request review" step saves the work somewhere the run page does not read. Second, the request is hard to find: the run looks "done" everywhere, and the only signal is an Inbox item that exists only when the request succeeded. Third, merging from the preview leaves the branch open, because the Studio merge button never tells the agent's work session that it was merged.

The code changed on 28–29 September. Autonomous runs moved onto work sessions, the same save/merge/discard model chat uses (FIMO-2429). That code is on develop and on staging (main), but not on production yet. Production still runs the older flow, which has the same three symptoms from slightly different causes. The fix below targets the new code, because that is what the next production release ships.

One more production bug is independent of the rewrite. Every new "Welcome to Fimo" project starts with a prepared footer suggestion from the welcome-guide agent, and none of them ever reaches anyone's Inbox. That is 726 invisible review requests in three weeks.

The intended journey

A review request should be visible, inspectable, and finish cleanly

An autonomous agent works on its own copy of the project, a task/… branch. When it finishes, it either merges (only with the auto-merge permission), throws the work away, or asks a person to review it. For the review case, a person should get a signal, open the run, see exactly what changed, and accept or reject it. After acceptance, the task branch should go away.

The diagram shows that journey. Red marks where it breaks today.

flowchart TB
  Agent[Agent works on<br/>task branch] --> Ask[Agent calls<br/>request_review]
  Ask --> Signal[Person gets<br/>a signal]
  Signal --> Run[Run page shows<br/>the changes]
  Run --> Merge[Person merges]
  Merge --> Close[Task branch<br/>closed]
  classDef broken stroke:#e5484d,stroke-width:3px;
  class Signal,Run,Close broken
Intended journey. Signal, run page, and close are the three broken steps.

Gap 1 · Changes do not show

"Request review" saves the diff where the run page never looks

There are two places where a review can be stored. The run review sits on the run itself (agent_sessions.reviewSummary), and the run page reads only that. The proposal is a separate record that the Inbox links to. When the agent calls request_review, the code creates the proposal and the Inbox item, but it never writes the run review.

So the run page finds no review. Its Code / Content / Schema tabs stay disabled, and the "Accept changes" button never appears. The only path that writes a run review is the one where the agent merges or discards the work itself. That is exactly the case where nobody needs to review anything.

The onboarding "prepared run" already does both steps. The normal agent path just forgot the run-review half.

request_review writesToday
proposal row
yes, linked to the run id
Inbox item
yes, if the call succeeds
run review
no, so the run page has nothing to show
merge / discard writesToday
run review
yes, captured before closing
publication
merged, conflict, or not published
side effect
a discarded run shows "Accept changes" for a deleted branch
apps/api/src/jobs/run-agent-session.tsThe only place a run review is captured: on close
2261async close(input) {
227  const session = await workSessionService.getOwned(input);
228  if (session.status === 'ready' && session.workingBranchName) {
2292    await captureSessionReview({ … })
236  }
237  const outcome = await workSessionService.close(input);
2383  await agentsBoot.reviewStore.markPublication(…)
  1. 1

    Merge or discard only. This wrapper runs when the agent closes its work session. request_review goes through review/capture.ts instead and never reaches it.

  2. 2

    The run page's data. This is what fills reviewSummary and the stored diff that the run page tabs render.

  3. 3

    Discard also marks "not published". The run page reads that as "waiting for review" and offers "Accept changes" for a branch that no longer exists.

Gap 2 · Request is invisible

Everything except one Inbox item says the run is "done"

With no run review, the rest of Studio also misreads the run. The status badge shows done, not needs attention. The runs-list "needs attention" filter skips it. The run notification cannot say "kept for review", because the code that builds it only knows three outcomes: merged, conflict, or nothing.

The only signal left is the Inbox item that request_review creates. It only exists if the call succeeded. The call can fail quietly: the agent gets requested: false back, and the person gets nothing. It fails on a diff time-out, when the branch moved mid-capture, at branch capacity, or on a target-branch mismatch.

Production has a second, bigger version of this problem: the onboarding welcome-guide run. It is prepared while the welcome project still sits unclaimed in the pool, with no members. At that point nobody can receive the Inbox item, and nothing creates it later when a user claims the project.

VerifiedProd · welcome-guide

726 of 745 retained runs with changes in the last 21 days have no Inbox item. All 726 are welcome-guide, one per new welcome project.

VerifiedProd · real agents

19 retained reviews from real agents did get an agent_retained_review Inbox item (old flow).

VerifiedStaging · new flow

13 runs since 28 Sep. The only retained welcome-guide run again has no Inbox item.

UnknownDid the reported run's request_review fail?

Needs the affected project id to check the run's tool-call outcomes.

Gap 3 · Branch stays open

Merging from the preview never tells the work session it is done

"Open preview" on the run opens Studio on the agent's task/… branch. From there, the normal Merge button runs a plain branch merge. That merge succeeds, but nobody tells the agent's work session. The session stays open, so its branch stays alive.

There is a cleanup hook, but it quits early: it only acts when the session already has a mergeId. The session gets a mergeId only when it closes itself. A Studio merge is a different merge, so the check always fails. A review session is also marked "keep forever" (no expiry), so the idle sweep never removes it either.

After the merge, the dialog offers "Keep branch" or "Delete". Since 3 Sep, "Keep branch" is the highlighted default. "Delete" closes the session, but records it as discarded, not merged.

apps/api/src/domain/work-sessions/service.tsWhy the merge cleanup does nothing
617async reconcileCompletedMerge(input) {
618  const session = await this.store.findById(input.sessionId);
6191  if (!session || … || session.status !== 'ready' || !session.mergeId)
620    return false;
6212  const snapshot = await this.deps.readMerge({ …, mergeId: session.mergeId });
622  if (snapshot?.status !== 'complete') return false;
  1. 1

    Studio merges never set this. Only the session's own close or resolve writes mergeId, so a person's merge from the preview exits here.

  2. 2

    Wrong merge. Even with a mergeId from an earlier parked conflict, it checks that old merge, not the one the person just completed.

VerifiedProd · old flow, same symptom

19 of 19 runs merged through the Studio merge dialog in the last 21 days still have an active task branch.

Smaller gap · Live refresh

The run page stops listening before the interesting part

The run page polls every five seconds while the run is active and stops when it finishes. After that it relies on a live agent:run event. That event fires only when the run starts, finishes, fails, or is cancelled. Nothing fires when a review is requested, when the work is merged, or when the publication state changes, so the page shows stale state until a reload or tab refocus.

apps/ui/src/features/agents/api/useGetAgentSession.tsPolling ends when the run finishes
refetchInterval: (query) =>
  isAgentSessionActive(query.state.data?.status) ? ACTIVE_SESSION_REFETCH_MS : false,
  • PollingStops once the run is finished (useGetAgentSession.ts).
  • EventsOnly planned, started, completed, failed, cancelled (run-events.ts).

Proposal

Five focused fixes, each closing one gap

Each fix reuses a path that already exists and works somewhere else. None adds a new concept. Fixes 1 and 2 matter most, and between them they cover all three symptoms.

The order matters: fix 1 makes the review visible, and fix 2 makes it finish cleanly. Fixes 3 and 4 remove the confusing leftovers. Fix 5 is a production-only onboarding bug that affects every new welcome project. Each fix ships with E2E that runs the journey in its row, including the failure case.

01
request_review also writes the run review
Starts with
The agent finishes work and calls request_review.
You do
Open the run from the Inbox or the runs list.
You see
Code / Content / Schema tabs with the diff, a needs attention badge, and "Accept changes".
Check
Capture the run review and mark it not_published in the same step, exactly as the onboarding prepared run already does.
Not proven
Not built yet. E2E must drive a real agent run to a review.
02
A Studio merge of an agent branch closes its work session
Starts with
A review is waiting on a task/… branch.
You do
Open preview and press Merge.
You see
The work lands on main, the run shows merged, and the task branch is gone.
Check
On merge completed, find the ready work session for that source branch, close it as merged with this merge id, and delete the branch.
Not proven
Not built yet. The failure path (merge with conflicts) must keep the branch.
03
Refresh the run page when the review state changes
Starts with
The run page is open on a finished run.
Trigger
A review is requested, or the work is merged.
Result
The page updates without a reload.
Check
Broadcast the existing agent:run event after these writes.
Not proven
Not built yet.
04
No "Accept changes" on discarded work
Starts with
The agent discarded its work.
You do
Open the run.
You see
The diff for reference, but no accept button.
Check
A discard is not "waiting for review".
Not proven
Not built yet.
05
The welcome suggestion reaches the new owner's Inbox
Starts with
A user claims a "Welcome to Fimo" project.
You do
Open the Inbox.
You see
"A footer update is ready to review".
Check
Create the review Inbox item when the claim finishes, once the project has members.
Not proven
Not built yet. Existing claimed projects stay without the item unless we backfill.

Evidence and limits

Diagnosis is grounded; nothing is fixed or tested yet

All findings come from reading develop at 8159c88ae, plus read-only queries against the production and staging databases. No code changed, and no test or E2E ran. The file list shows where each gap lives. Open a row to read the file at that revision.

Where each gap lives8 files
⌄▱apps/api/src
⌄▱apps/ui/src/features/agents
VerifiedSource reading

All three gaps traced to specific lines on develop 8159c88ae.

VerifiedProd and staging data

Read-only aggregate queries, run on 2026-09-30.

UnknownReported project

Not identified yet; exact failure of that run unconfirmed.

UnknownFix, tests, E2E

Not started.

Source peek

Open at head
Path copied