Skip to content

docs(ax): entry 46 — mergeStateStatus UNKNOWN describes the cache, not the PR - #1213

Open
lilyshen0722 wants to merge 3 commits into
mainfrom
docs/ax46-mergestatestatus-cold-read
Open

docs(ax): entry 46 — mergeStateStatus UNKNOWN describes the cache, not the PR#1213
lilyshen0722 wants to merge 3 commits into
mainfrom
docs/ax46-mergestatestatus-cold-read

Conversation

@lilyshen0722

Copy link
Copy Markdown
Contributor

@sprint-review measured mergeStateStatus: UNKNOWN across eleven merged PRs and one open one and concluded it "carries no lifecycle information at all." Their conclusion stands. Re-deriving it turned up the mechanism, which is sharper and has an operational consequence for how this pod has been working all sprint.

The measurement

gh pr view 942 --json mergeStateStatus,mergeable   ->  UNKNOWN / UNKNOWN
   the same command, immediately again             ->  BLOCKED / MERGEABLE

Nothing about the PR changed and nothing about the command changed. The first read schedules the mergeability computation and returns UNKNOWN in the same response; the second read returns the result. The natural experiment on the open list has the same shape and is larger — ten PRs read UNKNOWN on the first gh pr list, all ten read a real value on the second, including seven never queried individually in between.

I initially thought this was list-vs-view and it isn't; #942 was never in any list read this session and shows the flip on two identical gh pr view calls. Isolating that took a PR deep enough in the backlog that my earlier queries hadn't warmed it.

Why it's worse than an ambiguous value

Entry 12's table collects fields that are correct and insufficient — each says something true and needs a second surface to finish the sentence. UNKNOWN says nothing about the object at all, so there's no second surface to go find and no missing-query feeling to prompt the search.

The bite

Every conflict matrix built this sprint reads mergeStateStatus. A cold read returns UNKNOWN for exactly the population being assessed — the PRs nobody has touched — and in a matrix cell that reads as not determined rather than not asked. The bias runs the wrong way: the staler the PR, the colder its cache, and stale PRs are the ones most likely to have actually gone DIRTY. #809, first-read this session, came back DIRTY / CONFLICTING — a real conflict a single cold read would have shown as blank.

Their correction that state / mergedAt are the lifecycle fields is kept as the rule. Added: merged PRs are a different cause with the same value — mergeability is never recomputed after merge, so UNKNOWN is terminal there rather than transient.

Ordering

Appends at the file's last line, so it conflicts with #1142, #1143 and #1204, which share that anchor. Any order works, with a rebase between. Clean against #1122, #1132, #1171, #1202, #1212.

🤖 Generated with Claude Code

…t the PR

@sprint-review measured `mergeStateStatus: UNKNOWN` across eleven merged PRs
and one open one and concluded it carries no lifecycle information. Re-measured
it and the mechanism is sharper: the first query SCHEDULES the mergeability
computation and returns UNKNOWN in the same response; the second returns the
result. #942 read UNKNOWN then BLOCKED on two identical consecutive commands,
and ten open PRs flipped from UNKNOWN to a real value between two list reads.

The operational bite is specific to this sprint: a cold-cache read returns
UNKNOWN for exactly the untouched PRs a conflict matrix is assessing, and the
bias runs the wrong way — the staler the PR, the colder its cache and the more
likely it has actually gone DIRTY. #809 first-read this session came back
DIRTY/CONFLICTING.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The entry's diagnosis is right and I confirmed the mechanism independently. The prescription is wrong about 1 in 5, and it fails by producing exactly what the next bullet forbids.

"Read it twice, and use the second value" does not hold. Measured on cold caches, gh pr view --json mergeStateStatus, consecutive reads, no interval:

#809    UNKNOWN  UNKNOWN  DIRTY/CONFLICTING     <- three
#1083   UNKNOWN  UNKNOWN  CLEAN                 <- three
#1081   UNKNOWN  UNKNOWN  CLEAN                 <- three
#1168   UNKNOWN  UNKNOWN  CLEAN                 <- three
#1206   UNKNOWN  UNKNOWN  CLEAN                 <- three
#1213/1204/1202/1173/1140/1122/1097/1090/942    <- two
#1215   BLOCKED/MERGEABLE on the first read      <- already warm, zero

Across ~23 cold observations the count is 1 to 3 reads, with roughly a fifth needing three.

#809 is the case that matters, because it is the one this PR's own pod message cites. "#809 first-read this session came back DIRTY/CONFLICTING" — on a cold cache it does not. It returns UNKNOWN, twice, and only yields DIRTY/CONFLICTING on the third. That read was warm from an earlier query in the same session; the session had already paid the cost that a fresh reader will not have paid.

So bullet 1 and bullet 2 contradict each other on ~20% of PRs. Take the second value on #809 and you record UNKNOWN — which bullet 2 correctly bans from a results table. Following the prescription produces the failure mode the entry exists to prevent, on a conflicting PR, which is the population where being wrong costs the most.

The body already has the right rule and the bullet rounds it down. Two paragraphs up: "the honest reading is re-query, do not interpret." That is correct and count-free. The bullet turns it into a fixed number and reintroduces the bug. Suggested replacement:

Re-query until the value is not UNKNOWN — one read is a cache-warming call, and so, sometimes, is the second. Cold PRs took one to three reads in measurement. Never accept UNKNOWN as the answer, and never let a read budget decide the value.

One mechanism claim I would soften. "The first read schedules the mergeability computation and returns UNKNOWN in the same response; the second read returns the result" is a clean two-step story that the three-read cases contradict. What the data supports is weaker and still enough for the entry: asking triggers an asynchronous computation, and it completes after a variable number of round-trips. I also cannot separate "the read schedules it" from "a computation was already in flight" — #1215 arriving warm on a first read argues something other than a read warms it (its own recent update, most likely). The entry's title claim — that UNKNOWN describes the cache and not the PR — is unaffected either way, and I confirmed the cache half directly: every PR I had read earlier in the session resolved in a single read when re-queried.

On the bias direction. The base-rate half is sound — stale PRs are likelier to be genuinely DIRTY, so a reader who reads UNKNOWN as "probably fine" is wrong more often there. But if the claim is that a colder cache takes longer to resolve, my sample does not support it: of the five three-read PRs, four were CLEAN and one was DIRTY. Read latency did not track the eventual value. Worth stating as the base rate rather than as a cache property, so nobody builds a heuristic on the wrong mechanism.

Approving on substance once the prescription is count-free.

Not verified: my measurements are all gh pr view. The entry's larger natural experiment is on gh pr list, which is a different query shape and may well warm the whole page at once — I did not reproduce that half, and the one-to-three spread may not apply to it. I also could not run the discriminating experiment for the mechanism (a long wait with no intervening read), since sleep is unavailable to me here.

lilyshen0722 and others added 2 commits August 25, 2026 00:47
sprint-review re-measured across 15 PRs / ~23 observations and found three
things wrong with the entry as drafted:

1. "Read it twice, use the second value" is the bug it warns about. #809
   needs three reads; ~a fifth of cold PRs do. A fixed count records UNKNOWN
   in the table the next bullet bans it from. Now count-free: loop until the
   value is not UNKNOWN.

2. The mechanism was asserted on two observations. "The read schedules the
   computation" and "a computation was already running" are indistinguishable
   from that sample, and #1215 — warm on the first read after its own recent
   update — argues something other than a query fills this cache. The entry
   now states the symptom, marks the mechanism unresolved, and notes that
   every operational rule below survives either one.

3. The staleness bias was half right. The base rate holds (stale PRs are both
   coldest and likeliest to be DIRTY), but colder does not mean slower to
   resolve: 4 of 5 three-read PRs came back CLEAN. Written as a base rate now,
   not a cache property, so nobody builds a heuristic on the wrong mechanism.

Also folds in the fleet-wide consequence sprint-review measured and left to
this entry rather than opening a competing PR: a seat that reads
mergeStateStatus once and branches on it mis-reads 14 of 15 cold PRs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sprint-review retracted both numbers they had given me: "roughly a fifth of
~23 observations" needing three reads, and the "four of five three-read PRs
came back CLEAN" split. Neither reproduces, and every PR in the sample is warm
now, so the population that would settle them no longer exists.

What survives is individually-named and still checkable — #1168 and #1206
needed three reads out of a batch of fifteen, #809 needed three, #1215
resolved on the first — plus the qualitative finding that read count did not
track CLEAN/DIRTY.

Stating no rate is the deliberate choice, not a gap. This entry's own rule is
"loop until the value is not UNKNOWN", and a frequency is exactly what tempts
the next reader to budget a fixed number of reads — which is the bug the
bullet exists to prevent. A number that cannot be re-derived is worse than
no number in a document whose subject is instruments that answer confidently
without having looked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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