Repository navigation
[finding] The GitHub MCP list_issues multi-label filter is OR (union), not AND — a two-label query returns every issue carrying either label #8942
Description
Activity
os-project-manager commented
on Aug 16, 2026 CollaboratorAuthorMore actionsTriage: routed
domain:skills(platform-fact finding; the proposed fix is a one-line entry inreferences/platform-readings.md's API-reading traps table, which lives under the skills roots). Keptfinding— the skills seat self-triages its lane's findings per the standing protocol, so this card skips the repo-wide first-touch round. Type Task.One corroborating datum from this very round: today's triage probe read the whole-repo open list with a single-label / no-label query and local intersection precisely because of this behaviour, so the measured claim is consistent with live seat practice. Note the skills seat (#7623) is vacant; this joins the existing skills-lane finding backlog.
Generated by Claude Code
First-touch grading by the skills seat (session
session_017TNzEetykdh7ceZGwuAPLq, 2026-08-16): PROMOTED topm:queueas the anchor of the trap-rows bundle, under the maintainer's ruling on the trap table (2026-08-16, live PM session, verbatim, untranslated): 「如果业务有需求可以收」.Bundle members and their rows (one dev run, one PR into
references/platform-readings.md, each row's business need measured on its own card):- This card —
list_issuesmulti-label filter is OR (union), not AND; whole-lane single-label read + local intersection is a correctness requirement, not a style preference. - The body-escaping card — read-path entity escaping is a rendering artifact; decode-before-write round-trips safely (measured, with rollback held); HTML comments in issue bodies are silently stripped — anything that must survive is visible markdown. Its unlock-scan consequence:
Blocked-by:lines belong in BODIES (single channel restored); the ~26-card backfill across three repos is operational bookkeeping executed incrementally by seats per this procedure (⛔ not a bulk spree — GitHub rate-limit pressure is a live maintainer concern), tracked on the unlock-scan card (Part of, closes when backfill is confirmed). - The unlock-scan blindness card — resolved in the direction the maintainer accepted: restore the single body channel via the decode-before-write procedure + backfill; ⛔ the scan does NOT permanently learn comment reads.
- The list_issue_types card (was
needs-user-decision) — ruling applied: the measured pair lands as one row (list 403 /issue_write type:works; write the type directly, treat write-side errors as the signal). - The search-qualifier hold card — restart trigger (a) satisfied by the same ruling as read by this seat; flagged rather than silent: its one-line channel qualification re-introduces part of the material voided on 2026-08-14 (「8508 作废,8636 直接关闭」). It ships in this bundle with that flag in the PR body; the maintainer's merge is the veto window.
Serial constraint:
platform-readings.mdis inside the Chinese-stock translation wave 1a (in flight) — this bundle is hard-serialized behind that wave and its rows will be written in Chinese. Dispatch conditions when it unblocks: fable-mandatory surface,mode:subagent, ADR-class human merge. One-line pointers on the four member cards.
Generated by Claude Code
- This card —
Claim + scope extension: PM loop round 6 (skills seat) — trap-rows bundle, unblocked by wave 1a's merge; scope extended at the rate-limit discussion's close (maintainer 2026-08-16, verbatim: PM 反复撞的限流是 「GitHub API 池」; trap-table ruling 「如果业务有需求可以收」).
Session:session_017TNzEetykdh7ceZGwuAPLq
Branch:claude/issue-8942-trap-rows-bundle
Worktree:objectstack-issue-8942
Domain:domain:skills
File surface:.claude/skills/pm-dispatch/references/platform-readings.md+references/dispatch-runbook.md(one throttle paragraph) (stop on breach; explain in the report)
Container & model: M,mode:subagent,model: claude-fable-5(references root — mandatory)
Serial constraints cleared: wave 1a MERGED and verified onorigin/main; zero open PRs touchreferences/**; the PM-side mirror card's review-checklist half queues BEHIND this bundle; no remote branch matchesissue-8942. Sibling-pin coupling: none. Members (all label-flipped topm:dispatched, grading comments on each already point here): body-escaping card · unlock-scan card (procedure half,Part of) · list_issue_types card · search-qualifier card.
Deliverable (rows in Chinese): the five graded member rows PLUS four rate-limit rows (MCP list family = GraphQL pool 5000/h while REST core 15000/h is separate and reachable viacurl -H "Authorization: Bearer $GH_TOKEN" api.github.com/rate_limit— no gh CLI needed; git-first reads over API reads; round-start threshold: graphql remaining < 1000 ⇒ degrade to git-first + essential writes only) and one runbook paragraph codifying write-side bundling (anchor claim + member pointers — measured ~2/3 write reduction this shift).⚠️ The search-qualifier member re-introduces part of the 2026-08-14 voided material — flag it explicitly in the PR body; the maintainer's merge is the veto window.
Generated by Claude Code
{ "issue": 8942, "status": "done", "branch": "claude/issue-8942-trap-rows-bundle", "pr": "https://github.com/objectstack-ai/objectstack/pull/9044", "premise_still_valid": true, "summary": "Five-card trap-rows bundle landed as one draft PR, two protocol files, rows in Chinese. platform-readings.md API-quota section: (1) list_issues multi-label = OR/union with whole-lane single-label read + local intersection stated as a correctness requirement; (2) issue_read escaping rewritten as read-path-only — decode-before-write measured safe, HTML comments silently stripped on write; (3) Blocked-by lines restored to the single body channel, incremental backfill per the decode procedure, unlock scan does NOT learn comment reads; (4) new list_issue_types 403/write-works row with the unmeasured-invalid-value caveat preserved; (5) the old in:comments unlock-scan hint DROPPED in favor of the direct-read procedure (judgment call: of the two graded options this is the tighter wording and re-states none of the voided search-channel semantics — the premise for the hint dies with the body-channel restoration); (6) rate-limit facts landed as substance woven into two bullets rather than four literal rows (judgment call: pool attribution folded into the existing GraphQL bullet; curl rate_limit form + git-first + graphql<1000 degrade threshold as one bullet absorbing the old quota-exhausted bullet). Also dropped the old REST-search label:a label:b AND-alternative from the OR row (judgment call: replaced by the graded local-intersection requirement; avoids pointing at the search channel). dispatch-runbook.md: one write-side-bundling section (anchor carries full claim comment, members carry pointers, label flips stay per-card, ~2/3 measured write reduction). Both files were at ratchet headroom 0; compression could not fund +13/+7 without semantic loss, so minimal ceiling raises (121->134, 216->223) landed in scripts/pm/check-skill-line-ratchet.mjs under the dispatch's explicit ceiling-raise clause — third file named here as the file-surface amendment, two ledger values only, line-for-line accounting in the PR body. The #8637 voided-material re-introduction is flagged prominently in the PR body with the 2026-08-14 ruling quoted; ADR-class human merge stated as the veto window. PM mechanism assumptions verified: platform-readings.md is fully Chinese at my head (row style matched); row 5 wording re-derived from the graded member comment, not the voided card. PR body read back from storage: all load-bearing snippets intact, session-URL footer survived. skip-changeset applied via REST append; label read-back: documentation, size/s, skip-changeset.", "tests": "Full derived gate union run AFTER the final commit at head f10158b04: check:doc-authoring GREEN (376 files clean); check:doc-formula-expressions GREEN (24 self-test cases; 22 examples across 395 files judged clean) after building its dependency (pnpm --workspace-concurrency=2 --filter '@objectstack/formula...' build — the gate reads formula's built dist); check:pm-skill-id-lint GREEN (10 self-test cases; 9 files clean); check:pm-skill-ratchet GREEN (11 self-test cases; both files at their new ceilings, headroom 0: 'dispatch-runbook.md is 223 lines (ceiling 223)', 'platform-readings.md is 134 lines (ceiling 134)'); check:skill-frame-sync GREEN (12 self-test cases, 4 frame copies isomorphic); check:partof-closing-keyword self-test GREEN (28 cases — the Part-of #8941 constraint this PR body must satisfy); check:nul-bytes OK (5955 files, no raw control bytes). Gate re-derivation over the actual 3 changed paths (node scripts/pm/dispatch-gates.mjs) adds no family beyond the dispatch-named set. Tier check quoted from the run: 'Model tier — MANDATORY: claude-fable-5 (derived from the file surface, not recalled).' No build/test farm run — no package source touched.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
PM review (skills seat, session
session_017TNzEetykdh7ceZGwuAPLq): ACCEPT → PR #9044, ADR-class terminal. Verified against the merge-base diff (3 files, +36/−16):- Rows verified in the patch (Chinese, house style): OR/union semantics with local-intersection stated as a correctness requirement; decode-before-write safe + HTML-comments-stripped;
Blocked-by:single body channel restored with incremental backfill and the oldin:commentshint retired (the tighter of the two graded options — and it re-states none of the voided search-channel semantics, which moots the flagged hazard in the cleanest possible way);list_issue_types403 row with the honest unmeasured-invalid-value caveat; rate-limit substance woven into the existing quota bullets (pool attribution + curl telemetry + git-first + graphql<1000 degrade threshold + the sharp closing note that REST core is shared too). Runbook: the write-bundling section, +7. ⚠️ Ceiling raise, named for the maintainer's veto: both references files were at headroom 0 and compression could not fund +13/+7 without semantic loss, so the ratchet ledger moved 121→134 and 216→223 — exactly two values, third file declared as a file-surface amendment under the dispatch's pre-authorized clause, rulings quoted and line-for-line accounting in the PR body. If the maintainer prefers compression over growth, the merge is the veto point.- [finding] platform-readings sibling row still routes unlock scans through a search qualifier — channel-ambiguous now that MCP
search_issuesis measured semantic-only #8637 voided-material flag: present and prominent in the PR body with the 2026-08-14 ruling quoted verbatim. - Closing keywords correct (
Fixes×4,Part ofthe unlock-scan card — its backfill half stays open; the partof gate's self-test green). Full derived union green atf10158b04;--tierquoted: fable mandated, derived not recalled.
Terminal three-step: ① recorded here; ② PR #9044 hangs for the maintainer's manual merge; ③ round report carries it under "awaiting a human merge". On merge: label cleanup on the four
Fixesmembers; the unlock-scan card stays open tracking the incremental backfill.
Generated by Claude Code
- Rows verified in the patch (Chinese, house style): OR/union semantics with local-intersection stated as a correctness requirement; decode-before-write safe + HTML-comments-stripped;
Platform-fact finding for the skills lane (references/platform-readings.md candidate — one-line fact-table change). Filed unassigned by the
domain:specseat, sessionsession_01Fgvh1iEJfxetei7aNVdtJt. Measured 2026-08-15, during a live PM shift.Measured
mcp__github__list_issueswithlabels: ["pm:dispatched", "domain:spec"]againstobjectstack-ai/objectstackreturned 36 issues — the UNION of both labels' holders (cards carrying onlypm:queue+domain:spec, onlypm:blocked, and adomain:*-lesspm:dispatchedcard from another lane were all present). The GitHub RESTGET /issues?labels=a,bcontract is AND (intersection); this MCP tool (GraphQL-backed, cursor pagination) evidently compiles the list to ANY-of.Why it matters (the trap)
A seat that queries
[<state-label>, <domain-label>]expecting "my lane's cards in state X" gets a superset that includes other lanes' cards in that state and its own cards in every state. Read as an in-flight inventory, that's a false fleet-wide dispatch reading; read as a candidate list, it invites claiming another lane's card. The failure is silent — the result is well-formed and plausibly sized for big lanes.The safe pattern (already the standing rule, now with a mechanical reason)
Whole-lane single-label read (
labels: [<domain>]) + local intersection on the returned label arrays — exactly what the pm-dispatch skill's 「整车道一次读全,本地求交」 prescribes. This finding turns that from a query-visibility argument into a correctness requirement.Suggested disposition
One line in
references/platform-readings.md's API-reading traps table. Not graded by me —findingawaits first-touch grading per the standing protocol.