feat(inbox): file a draft once you reviewed the PR on GitHub yourself - #29
Merged
Conversation
A review submitted through GitHub's own button never reaches cerber, so the draft it wrote for that PR went on sitting in the inbox as work still waiting on you — days after the request had cleared and the PR had gone back to its author. The queue could only see what you settled locally; the one thing it could not see was you answering the PR somewhere else. So the poll asks the other question. For an artifact GitHub no longer requests you on, it reads the PR's reviews once every 30 minutes — the same leash the state check rides — and when one of yours stands there, files the draft under settled with a record of which review settled it. Nothing is sent, nothing is deleted: the draft is still there to open, re-review or send, and the cockpit says "reviewed on GitHub" with the date rather than claiming a click you never made. A pending review is not one anyone has seen and a dismissed one is one GitHub struck off, so neither counts. Runs now record who asked for them. A draft you requested yourself after that review — pasted the URL in, pressed re-review — is a second opinion you went and got, and filing it away would answer a question you had just posed; only a draft the poll wrote on its own is filed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR teaches cerber to automatically file a local draft under “settled” when GitHub is no longer requesting your review and GitHub already shows a submitted review from you—covering the case where you reviewed directly on GitHub rather than via cerber’s Send path.
Changes:
- Add a
filedmetadata block to artifacts / inbox rows to record when/why a draft was filed (and which GitHub review triggered it). - Extend the daemon poll to fetch your latest standing PR review from GitHub and file eligible drafts, guarded by a new
run.triggerfield to avoid filing drafts the user explicitly requested after that review. - Update the web UI to surface “reviewed on GitHub” tags/banners, and add unit tests + documentation for the new behavior.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| web/src/types.ts | Adds Filed and wires filed onto inbox rows and artifacts for UI consumption. |
| web/src/inbox.ts | Displays new “reviewed on GitHub” row/tag behavior and explanatory strip text when filed is present. |
| web/src/inbox.test.ts | Adds coverage for row tagging / strip messaging / open-request tag behavior for filed drafts. |
| web/src/Detail.tsx | Shows a banner on the detail view when a draft was filed due to a GitHub review. |
| src/server/index.ts | Ensures newly created/rerun artifacts record run.trigger: "user" and initializes filed: null; exposes filed in list items. |
| src/server/daemon.ts | Implements the “file if you reviewed on GitHub” rule and adds filedByOwnReview guard logic. |
| src/server/daemon.test.ts | Adds tests for filing behavior, trigger guard, and avoids GitHub calls when still requested. |
| src/server/chat.test.ts | Updates test fixtures for the new filed field. |
| src/runner/review.ts | Adds trigger option and persists it into artifact.run for daemon/user attribution. |
| src/runner/chat.test.ts | Updates test fixtures for the new filed field and run.trigger. |
| src/core/state.test.ts | Updates test fixtures for the new filed field and run.trigger. |
| src/core/send.test.ts | Updates test fixtures for the new filed field. |
| src/core/revise.test.ts | Updates test fixtures for the new filed field. |
| src/core/refresh.test.ts | Updates test fixtures for the new filed field. |
| src/core/gh.ts | Adds fetchOwnReview and latestOwnReview to detect your latest standing GitHub review. |
| src/core/gh.test.ts | Adds tests for latestOwnReview behavior (ignores PENDING/DISMISSED, picks latest standing review). |
| src/core/autosend.test.ts | Updates test fixtures for the new filed field. |
| src/core/artifact.ts | Extends the artifact schema with FiledInfo and run.trigger fields. |
| README.md | Documents the new “reviewed on GitHub → auto-filed” behavior. |
| CLAUDE.md | Updates architecture documentation to reflect the new daemon filing path and trigger guard. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Review feedback on #29, both real. `run.startedAt` carries milliseconds and GitHub's `submitted_at` does not, and ISO-8601 does not compare as text across precisions: "…:00.500Z" sorts *before* "…:00Z". So a re-review you asked for half a second after your own GitHub review read as older than it, and the guard that exists to protect exactly that draft would have filed it away. Parse both sides. The regression test pins the one case where the mismatch shows — same second on each side. The detail banner led with "cerber filed this draft on <date>" while rendering the date of your review, which is a different event. It now leads with the review and its date, and says the filing followed. Also states in `filedByOwnReview` why a run recorded before the trigger existed counts as the poll's: the alternative strands every draft written before this shipped, and being wrong costs one filing that a re-review undoes for good. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
🎉 This PR is included in version 0.21.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
You can review a PR the ordinary way — GitHub's own review button, cerber not
involved. That clears the review request, and nothing here hears about it.
The queue lists what you have not settled locally, so the draft cerber wrote
for that PR goes on sitting in the inbox as work still waiting on you, days
after the PR went back to its author. The daemon can't retire it either: it
only deletes rows that fall out of the awaiting search and hold no work
(
isPureStub), and a finished draft is work. Nothing but a merge, a send, or aclick of yours ever moves it.
It is not a corner case. A dry run of the rule below over the author's own
queue found three of five open drafts stuck exactly this way.
The fix
The poll now asks the other question too. For an artifact GitHub no longer
requests you on, it reads the PR's reviews and — when one of yours stands
there — files the draft under settled, recording which review settled it.
Nothing is sent and nothing is deleted. The status goes
ready → reviewed, thenew
filedblock keeps the review it acted on, and the draft is still yours toopen, re-review or send.
One guard keeps this from undoing a deliberate act. Runs now record who asked
for them (
run.trigger): a draft you requested after that review — pasted theURL in, pressed re-review — is a second opinion you went and got, so it is left
alone. Only a draft the poll wrote on its own is filed.
Details
fetchOwnReview/latestOwnReview(src/core/gh.ts) — the read, and thepure part of it. A PENDING review is one nobody has seen (no
submitted_at,and GitHub still asks you); a DISMISSED one is a review a maintainer struck
off. Neither counts.
filedByOwnReview(src/server/daemon.ts) — the whole rule, exported andunit-tested: a finished unsent draft, a review of yours, and no user-requested
run since that review.
syncQueue— at most 10artifacts per poll, once each per 30 minutes — so it is one extra
gh apicall on rows that qualify and none on the rest. Rows GitHub still requests you
on never reach it.
reviewed on GitHub,never
reviewed— that would claim a click you never made. The strip namesthe date, and opening the review shows a banner linking your review on the PR.
requestTaglearned the same fact. When the author later re-requests you,a filed row reappears under open requests, where the conversation read would
have tagged it "you haven't replied" at someone who demonstrably did — the
same class of bug
sentTagwas written for, reachable again through thispath.
filed: null, so re-drafting a filedreview un-files it with no extra machinery.
Verification
pnpm typecheck && pnpm test && pnpm build— green; 379 tests, 14 new.your review; leaves it while the request stands; leaves it when you never
reviewed; keeps a second opinion you asked for; never touches a sent review),
the review parse (PENDING, DISMISSED, latest-of-yours), and the three cockpit
readouts.
~/.cerber/reviews: three draftsmatched with a review of the user's own on each, two rows GitHub still
requests were left untouched.
Worth knowing
Not addressed here: the daemon can re-draft a PR minutes after you reviewed it
on GitHub, because
gh searchlags the API. Closing that would mean a reviewsread per awaiting PR on every poll — double the per-poll traffic to save a
few-minute window — so it is deliberately left out.