Skip to content

[finding] The unlock scan's enumeration reports completion over a list it did not finish reading — perPage 50 against 51 blocked cards, and the output cannot tell the two apart #12817

Description

@huangyiirene

Filed unassigned by the triage seat, session session_01Aujz2zykf5LXt3T98gRsGe. ⛔ Not claimed, not graded.

⚠️ This card was filed with the wrong claim and has been rewritten down. Read this section first.

As originally filed, this card claimed the unlock scan's body-only Blocked-by: read was a defect, and proposed that the scan read comments as well. That claim is retracted. Two things were missed at filing:

  1. [finding] the pm:blocked unlock scan greps issue BODIES only, while seats deliberately park Blocked-by: lines in COMMENTS — blocked cards go invisible and sat 24h+ past discharged upstreams #8941 already exists and is the same card — "the pm:blocked unlock scan greps issue BODIES only, while seats deliberately park Blocked-by: lines in COMMENTS" — filed 2026-08-16, closed completed 2026-08-24.

  2. It was resolved the other way, and the ruling is in .claude/skills/pm-dispatch/references/platform-readings.md:

    Blocked-by: 行归 BODY(单通道反向索引):追加按上条「解码后写回」执行;历史上寄放在评论里的行按同程序增量回填(⛔ 不搞批量突击 —— 限流压力);解锁扫描只 grep body,⛔ 不加常设评论读;旧「连评论一起扫(in:comments)」提示作废。

⇒ The body-only read is correct. The proposed "read comments too" fix is the rejected direction. The 69% of blocked cards without a body-level line is outstanding incremental backfill, deliberately not done as a batch — expected progress, not a defect.

⛔ Also retracted: the argument that a well-reasoned comment placement means the scan should follow it. That argument was considered by #8941 and decided against.

What survives is one defect that #8941 does not cover, and it is the whole of this card now.


The defect: an enumeration that cannot say it was truncated

The unlock scan enumerates open pm:blocked cards and reads their blockers. On 2026-08-27 it ran with:

list_issues(state=OPEN, labels=[pm:blocked], perPage=50, ...)
  -> issues: 50 returned
  -> totalCount: 51

One card was never examined. Nothing in the scan's own output distinguishes "read all 51" from "read 50 of 51" — the caller sees a list and proceeds. It was caught only because the count mismatch happened to be noticed by hand.

The missed card was #9709. Reading it produced a real finding: its body stated "blocked until PR #9706 lands", and PR #9706 merged 2026-08-19, nine days earlier. Its live blocker is the still-open epic #9465 — a different card the body never named in a machine-readable field.

⛔ Note carefully: nothing was lost. #9709 was still blocked, on the fence, so the discharged blocker would not have released it. The defect is not the outcome — it is that a scan reported completion over a list it had not finished reading, and no reader could have told.

Why this is the same class as #12411 / #12815

Those two cards are about dispatch-gates rendering "staleness could not be measured" identically to "this tree is current". This is the same collapse in a different instrument: an incomplete reading and a complete reading print the same thing. #12411 landed the fix for one half of its case (PR #12816); #12815 holds the other half. This card is that shape in the triage seat's own enumeration.

Shape of the fix — constraints, not a design

Backfill progress, recorded as a reading rather than a claim

Since the ruling's incremental backfill has no progress meter, this is one, measured 2026-08-27 across both authorized repos:

repo open pm:blocked body-level Blocked-by: outstanding
objectstack 50 19 31
objectui 38 8 30
total 88 27 (31%) 61 (69%)

Control run: a loose /Blocked[-\s]?by/i sweep found exactly one card matching loosely but not strictly (#6182), and it is a negation in prose ("is not blocked by this"), not a missed blocker ⇒ the strict extractor does not under-match on the bodies it reads.

⚠️ Two increments have since been performed by this seat, so the number above is already stale by two: #9709 (Blocked-by: #9465, backfilled 2026-08-28 with byte-level read-back) and #8103 (Blocked-by: #12804 — pending; deferred deliberately, see below).

One measured caveat for whoever does the next increment

The decode-then-write-back round trip was validated on #9709 and preserved everything checked — <-- inside a code fence, every escaped apostrophe, both code fences — except the trailing _Generated by [Claude Code]_ attribution footer, which was stripped. Bodies carrying that footer lose it on a backfill. Bodies without one (#8103 among them) are unaffected.

⛔ #8103's backfill was deliberately deferred one round rather than done opportunistically: it is a maintainer-ruled destructive sweep over a secrets table, its body carries the safety constraints on that deletion, and a full-body rewrite means retyping them. The procedure was proven on a low-stakes card first. That is the intended reading of "⛔ 不搞批量突击".

Duplicate check

#8941 is the parent of the retracted half and is closed; this card is deliberately not a duplicate of it after the rewrite — the enumeration-completeness defect is not mentioned there. Nearest live neighbours: #12813 (the ⑦ reader-count method, also a seat-procedure finding, also domain:skills) and #12815 (the same collapse class in dispatch-gates).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions