ai-coding-minesIndexGitHub

The watch queue is empty but the rejections exist

Write APIs

One queue watching an approval workflow produced three of these in three days. Each is silent: no exception, no log line, and an item leaves observation.

① The card's title promises more than its source filter returns

The dashboard card was titled "rejection watch," and its only source was the watch queue. That queue selects submitted and unknown and structurally excludes rejected. The card next to it showed "REJECTED: 2" while this one said "no rejections under watch." The screen contradicted its own title.

The queue was not widened (watch means "not yet judged," and widening blurs that). A separate read function feeds the card, which merges both and labels them apart.

A card title is a contract. When a filter narrows the meaning, narrow the title or widen the source. One of the two.

② Status was judged from a side signal instead of the authoritative field

The status verdict looked only at whether a comment existed. So a one-line memo that was not a rejection ("saved as draft," "reviewer assigned") set the item to rejected and pushed it out of the queue.

Two layers. A fallback returned the last comment whenever no rejection marker was present (the intent was "prevent silent omission"). And the verdict function, while already holding the authoritative state field, decided "rejected" if comment else "unknown" on the presence of text alone.

"There is a reason" and "it is rejected" are different propositions. A fallback meant to prevent silent omission turned non-rejections into rejections and produced a quieter omission. A safety net promoted to a verdict is itself a defect. Don't remove the fallback; narrow its job. The memo stays so a human can read it on screen; the status comes from the authoritative field only.

Beware the half-fix. Fixing the verdict alone is not a fix. If the queue's accepted-status list doesn't include the new unsettled state, the verdict is right and the row still leaves the queue. Verdict and queue list are two implementations of one idea, so bind them with a contract: the set of unsettled states is a subset of the states the queue accepts.

And the post-mortem didn't reach the bottom on the first pass. The first write-up said "this behavior is correct; the missing re-arm is the defect." That was wrong too. Fix one layer and the next one shows.

③ The remedy never updates the observed status

The resubmit function (re-request approval) sends the request to the marketplace and returns {success: true}. It doesn't touch the ledger's status. So apply the remedy to a row that has already left the queue (rejected): the request really goes out, the item really re-enters review, and the ledger still says rejected, so the next sweep never looks at it. Nobody checks the result.

The harder you apply the remedy, the further out of observation the item goes. That is the shape of this defect.

It is the reverse of chapter 02, "upsert rolls back workflow state." There a collector reset the status and erased a human's action; here an action leaves the status alone and kills the watch.

An action that changes state must also change the observed state. "The ledger records what the marketplace answered" is a sound principle, but *we asked again* is also a fact, and that fact decides whether the item is under observation.

Verification

Print one line per sweep: in queue N · graduated M · saved a · unknown b · approved c. Unless you count how many items sit in which state, none of the three is visible. And there is an order: after fixing the verdict, a ledger that already says rejected won't be caught on the first sweep. Re-arm first; it shows from the next sweep. Miss that order and you get "I fixed it, so why isn't it there?"