Skip to content

finding(pm-tooling): list_issues labels is a UNION, not an intersection — every multi-label board reading is wrong, and wrong in the direction that looks right #15121

Description

@huangyiirene

Filed unassigned by the triage seat (session session_019kDRpB7D2XzVzkaLp57T5D, R+131). Grading and any pm:* are the domain:skills seat's — that lane self-triages its own findings, so this card is deliberately left ungraded.

Measured 2026-09-04T00:52Z against objectstack-ai/objectui.

The defect

mcp__github__list_issues takes labels as an array. It reads as a conjunction — "issues carrying all of these" — and this seat has been reading it that way. It is a disjunction. The returned totalCount is the size of the union.

How it surfaced

Counting the objectui straggler class (cards carrying finding and a pm:* state):

query returned whole finding box
["finding","pm:queue"] 117 77
["finding","pm:blocked"] 120 77
["finding","needs-user-decision"] 92 77
["finding","pm:dispatched"] 83 77

⇒ Every "intersection" outnumbered its own superset, and three of the four returned the same first issue. A subset cannot exceed its superset; that is what made it visible at all.

The decisive control

Inclusion–exclusion fits, but a fit is not a proof — so: pm:dispatched and needs-user-decision are both members of the six-state pm:* ONE-OF, so their true intersection is ~0 by design. The two hypotheses therefore predict opposite answers:

  • AND ⇒ ~0
  • OR ⇒ 6 + 18 − 0 = 24

Measured: 24. ⇒ Union, settled.

Consistency across all six pairs

With the union model, |A ∪ B| = |A| + |B| − |A ∩ B| recovers a small non-negative integer every time — six for six, which an incorrect model would not do:

pair union singles implied ∩
finding, pm:queue 117 77 + 40 0
finding, pm:blocked 120 77 + 45 2
finding, needs-user-decision 92 77 + 18 3
finding, pm:dispatched 83 77 + 6 0
finding, pm:on-hold 143 77 + 66 0
finding, pm:retriage 78 77 + 1 0

Independent corroboration: those implied intersections total 5, and R+123 measured the same straggler class at 5 by a completely different route (enumerating cards). Two instruments, one answer.

Why this one is dangerous rather than merely wrong

A union is always ≥ each single, so a wrong answer here never looks absurd — it looks like a bigger, healthier-seeming board. It fails loudly only in the rare case that caught it: querying an intersection against a label whose own box you already know. Every other multi-label reading returns a plausible number that no sanity check rejects.

This is the same family as the search_issues readings in #15012 — an instrument that answers a different question than the one asked, with no error surface. ⛔ It is not a flake and ⛔ not intermittent: it reproduced on all seven calls.

Suggested shape

  1. Record it in .claude/skills/pm-dispatch/references/platform-readings.md — labels is a union; ⛔ never read a multi-label totalCount as an intersection.
  2. Canonical workaround, which this card demonstrates: measure each label singly, then derive the intersection by inclusion–exclusion. It costs one extra call per pair and its output is checkable (a negative or implausible result means the model broke).
  3. ⚠️ Worth a sweep of whether any other seat's standing readings rest on a multi-label list_issues. This seat cannot see other lanes' scratch work, so the blast radius is NOT MEASURED here.

⛔ Not measured: whether list_issues intends this (it may be GitHub's documented labels semantics surfacing unchanged), and whether the same holds for the labels filter on any other MCP tool. This card records the behaviour, not its cause.

Activity

  1. huangyiirene commented on Sep 4, 2026

    @huangyiirene
    CollaboratorAuthor

    Attribution for the card above — and an unplanned live reproduction of #13384.

    This card was created with the standard trailing rule + attribution footer. The write-then-read-back showed the body ends at "This card records the behaviour, not its cause." — the final --- and footer were silently stripped by issue_write on create. Nothing else was lost: all six tables, every ⇒ and ⛔ glyph, and both em-dash sections survived byte-intact.

    ⇒ A clean before/after for #13384, narrower than that card's general warning states it: what was dropped was exactly the trailing horizontal rule and the line after it, ⛔ not a truncation of arbitrary tail content. Carrying the attribution as a comment is the workaround; ⛔ re-editing the body would meet the same mechanism.


    Generated by Claude Code

  2. claude commented on Sep 4, 2026

    @claude
    Contributor

    Triage — skills seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve), 2026-09-04T12:18Z (the lane self-triages its own findings). Graded priority:p3, documentation, state pm:queue (domain:skills already set). Applied and read back after this comment.

    Disposition: a platform reading for .claude/skills/pm-dispatch/references/platform-readings.md — the MCP list_issues labels array is a UNION (the decisive control: two mutually exclusive pm:* states returned 24, the OR prediction), so every multi-label board reading taken through it over-counts; the REST list endpoint's labels parameter is the AND (this seat's board readings use it). It lands with the fourth platform-readings increment: the third (#15192, +39 → 397) is ruled and in its patch round, and the file has no headroom, so this member needs its own measured line cost and a ceiling ruling before dispatch — the same path #15192 took. ⛔ Not dispatchable until that decision card exists; queued so the next increment's measurement flight picks it up.


    Generated by Claude Code

  3. self-assigned this
    on Sep 5, 2026
  4. claude commented on Sep 5, 2026

    @claude
    Contributor

    Claim: PM loop round 5 — folded into #15379 member 3 (the rules-only rewrite of the readings ledger and its siblings); this card's reading lands as fact lines (the MCP list_issues labels array is a union, never an intersection; count singly and derive by inclusion–exclusion)
    Session: session_019RfFHiRCSs3JXLK4cwcfox
    Branch: claude/issue-15379-readings-lanes-rules-only (the family branch — a member card has no branch of its own by design)
    Worktree: objectstack-issue-15379-m3
    Domain: domain:skills
    File surface: .claude/skills/pm-dispatch/references/platform-readings.md (readings-trap section) — one to three fact lines, paid for inside the member's net shrink; no ceiling raise
    Container & model: L (the family flight), mode:subagent, model: opus (tier per the chain-head claim)
    Clause-②: no
    Serial constraints cleared: in the chain-head claim on #15379 (member 3, 2026-09-05T00:1xZ); the member's draft PR carries Fixes #15121 and lands governed (draft, in-seat review, os-zhuang + hotlong, human merge).


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions