Skip to content

pm 席位 GraphQL 限流:高频读改走 REST(curl+代理凭证),GraphQL 只留给 mutation-only 操作 #11181

Description

@os-zhuang

维护者 2026-08-23 指示:「项目经理经常被 GraphQL 限流……rest api 余额很多,排查哪些请求可用 REST,能用 REST 的尽量用 REST。」本卡由 skills 席(session 757f0dc0-5dae-51d7-9053-c15c57ebfe3b)带实测立卡。

实测(2026-08-23 01:1x UTC,共享身份,GET /rate_limit 免费端点)

桶 本小时 used limit
core(REST) ~50 15000
graphql ~4965 5000
search 0 30/min(独立桶)

两个结构性证据(不受并发席位噪声影响):

  1. MCP list_issues 返回 pageInfo.startCursor/endCursor = GraphQL Connection 游标——issue 列表走 GraphQL;
  2. 夹逼测量:MCP 调用间 graphql used +7/+4,core 几乎不动。

即:所有席位共用一个身份,GraphQL 桶是公共牧场且已在天花板上;REST 桶几乎全空。

路由表(PM 循环高频操作)

操作 现走 改走 REST
拉 backlog(按标签列 issue) list_issues(GraphQL 已证) GET /repos/{o}/{r}/issues?labels=…&state=open&per_page=…
决策重读(评论翻页到最后) issue_read get_comments GET /issues/{n}/comments?per_page=100&page=N
卡正文/状态读 issue_read get GET /issues/{n}
标签读-改-写 get_labels + POST GET /issues/{n}/labels + POST(POST 两条已在 #11112 进 allowlist)
PR 读(正文/文件/commits) pull_request_read GET /pulls/{n}、/files、/commits
CI 判定 get_check_runs GET /commits/{sha}/check-runs(本席今日已用)
PR 正文改写 update_pull_request PATCH /pulls/{n}(本席今日已用,含 sanitizer 尾读回)

保留 MCP/GraphQL(无 REST 等价或语义缺失):enable_pr_auto_merge(mutation)、resolve_review_thread / unresolve_review_thread、pull_request_read get_review_comments(线程 isResolved 态 REST 不给)。search_* 独立桶,不动。

交付物

  1. .claude/settings.json permissions.allow 增补 curl GET/PATCH 条目(比照 Make the additive label POST the prescribed write and permit it for dev agents #11112 的两条 POST 形状,objectstack+objectui 双仓),使席位免弹窗走 REST。⚠️ .claude/** 属 governed:draft PR + 人工合并。
  2. 路由指引落在哪:platform-readings.md 134/134 零余量、SKILL.md 682/682 零余量——允许以真删付账(优先挖重复),付不起就只落 allowlist、prose 部分显式移交 Whole-text restructuring round of the pm-dispatch protocol face — maintainer-ordered, fires when the skills-lane backlog drains #11086 全文重构轮(在 PR 正文声明)。
  3. 曾有席位汇报过被限流的现象记录可引用本卡实测,不必重测。

验证

  • allowlist 生效:worktree 内以 seat 权限跑一条表内 curl GET 不弹窗;
  • 不回归:.claude/hooks/* selftest 全绿;若动 prose,line-ratchet 门绿并在 PR 正文给付账明细。

Activity

  1. self-assigned this
    on Aug 23, 2026
  2. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    ContributorAuthor

    CLAIM — session 757f0dc0-5dae-51d7-9053-c15c57ebfe3b(skills 席),branch claude/issue-11181-pm-rest-routing。维护者 2026-08-23 当面指示立即执行,立卡即派发。


    Generated by Claude Code

  3. added a commit that references this issue on Aug 23, 2026
    99f2db4
  4. claude commented on Aug 23, 2026

    @claude
    Contributor
    {
      "issue": 11181,
      "status": "done",
      "branch": "claude/issue-11181-pm-rest-routing",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/11186",
      "premise_still_valid": true,
      "summary": "Deliverable 1 landed: `.claude/settings.json` `permissions.allow` gains 19 narrow curl entries — nine REST path shapes per repo as objectstack/objectui twins (issue list, issue get, issue comments, issue labels read, PR get, PR files, PR commits, commit check-runs, PATCH PR body) plus the free `GET /rate_limit`. Shape mirrors the #11112 additive-label POST exactly: method + host + path shape pinned, URL immediately after the `-X` flag, trailing ` *` absorbing the Bearer header and payload; no bare `curl *`. Deliverable 2 (routing prose) is DEFERRED to the #11086 whole-text round and declared as such in the PR body — `platform-readings.md` is 134/134 and SKILL.md 682/682, and no honest deletion was available (detail in open_questions). No ceiling was raised. PR is a DRAFT on a governed surface; no seat flips it ready, enqueues it, or arms auto-merge — read back as draft:true, auto_merge:null, base:main. `skip-changeset` applied via the additive POST and read back after 25s: labels = size/s, skip-changeset. Incidental confirmation of the card's premise: the PR body PATCH and every read-back in this task were done with real REST curl from this seat (HTTP 200), so the seat's direct-REST channel is live.",
      "tests": "Gate families derived with `node scripts/pm/dispatch-gates.mjs` with NO hand-fed paths — it read the change set itself (`1 path(s) vs merge base be7262e72`, working tree 1) and named 4 families. Union re-run on the FINAL commit `99f2db40e2` with a clean `git status`; every exit code captured before any pipe (redirect-then-capture), every verdict quoted from the gate's own line. ratchet --self-test EXIT=0 '19 cases pass'; ratchet EXIT=0, 20 green ceilings incl. 'platform-readings.md is 134 lines (ceiling 134; headroom 0)'; check:doc-authoring EXIT=0 '389 files clean — no bare metadata literals'; check:skill-frame-sync EXIT=0 '4 copies of the decision frame are structurally isomorphic across 3 files'; check:pm-governed-merges EXIT=0 '119 assertions'; check:doc-formula-expressions EXIT=0 '22 record-scoped formula example(s) across 416 files / 1447 TS blocks judged clean' (needed `pnpm install --filter @objectstack/lint...` and `pnpm --filter '@objectstack/lint^...' build` first, both under `scripts/pm/os-verify-lock.sh -c`, VERDICT command-exit 0, held 2s and 198s); check:nul-bytes EXIT=0 'scanned 6485 text file(s), no raw ASCII control bytes'. Hook selftests (not touched by this diff, run because the card asks): guard-main-checkout-bash EXIT=0, guard-shared-stash EXIT=0. governed predicate `check-governed-merges.mjs --test .claude/settings.json` EXIT=3, verdict line 'GOVERNED — a human merge is the review record for this PR'. JSON validity: parses, 47 allow entries / 21 curl. Repo-wide ESLint NOT run locally — declared narrowing with all three proofs in the PR body: (1) population read from `eslint.config.mjs` itself — `**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` plus five .ts-only blocks, no JSON plugin, no `files:` naming .json; (2) file count of the intersection with this diff = 0, the diff being exactly one .json path; (3) invariance — a permission allowlist defines no rule, plugin, parser or files pattern, so it cannot move any untouched file's verdict. No ablation was performed (nothing in this card asserts a gate can fail).",
      "open_questions": [
        {
          "question": "The card's 2026-08-23 measurement and an existing dated line in `platform-readings.md` § API 配额 contradict each other on seat identity, and a second line there is overturned by this task's own evidence. Both are measured readings; resolving them is a supersession edit, not a line-budget payment, so I did not land it inside an allowlist PR. Who resolves, and where?",
          "options": [
            "A — resolve in the #11086 whole-text restructuring round, where both lines are already in scope and a reviewer is reading the prose: replace the per-seat-accounts claim with the newer measurement (dated, both readings' provenance kept) and replace the 'a pure-MCP session has no list channel until reset' clause, whose premise this task disproved — the seat ran GET /rate_limit, GET issue/PR/check-runs and PATCH /pulls/11186 at HTTP 200 today. Costs nothing now; the stale lines keep misinforming seats until that round lands.",
            "B — file a dedicated follow-up card now for just these two lines, funded by deleting the superseded text (a genuine 真删, roughly the headroom the routing row needs). Fixes it sooner and self-funds the prose row, but spends a dispatch slot and duplicates #11086's reading pass over the same section.",
            "C — land the supersession as a rider on this PR. Rejected and not done: it would resolve a conflict between two dated measurements inside a permissions diff where no reviewer is looking for it, and it needs a ceiling payment I cannot honestly fund by rewrap."
          ],
          "recommendation": "A, because the conflict is between two dated measurements rather than a defect, and #11086 already has the whole-text reading pass scheduled over exactly this section — that is the round where a reviewer is positioned to judge which reading survives and to carry the routing row in with it. B only wins if seats are actively being misled before #11086 lands; the practical harm is bounded (the stale lines make seats MORE conservative about REST, not less), so it does not justify the extra slot. Note for triage either way: the allowlist landed here is independent of the outcome — it removes prompts, it instructs nobody."
        }
      ],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. added a commit that references this issue on Aug 23, 2026
    2b5a7ce
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