Skip to content

pm(skills): probe the unexercised CCR routes and the shared MCP user bucket into the REST channel register - #18259

Merged
claude[bot] merged 4 commits into
mainfrom
claude/issue-18130-ccr-routes-probed-into-the-register
Sep 15, 2026
Merged

claude[bot] merged 4 commits into
mainfrom
claude/issue-18130-ccr-routes-probed-into-the-register

Conversation

@claude

@claude claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Fixes #18130

Measurement-first register work on .claude/skills/pm-dispatch/references/rest-channel.md.
The card's readings 2 and 3 (ccr/ready_for_review, PUT ccr/auto_merge) were already on
:48 / :51 and are not re-recorded. What this branch owes is one probe per UNEXERCISED CCR
route named by the session proxy's own 403 body, plus the shared per-user MCP bucket fact.
Every route below earned its row by being invoked once; nothing here is written from the
card's text.

Session: session_01HZfg2AwVX191qCizp88gQr (skills seat, dev subagent). All probes ran on
THIS draft PR, 2026-09-15T04:48Z–04:49Z. No probe touched another seat's PR.

Probe log

# Route Method / body sent Status Body stored / returned Read-back
P1 https://api.github.com/graphql POST, markPullRequestReadyForReview mutation 403 proxy's own body, 629 B, naming the five CCR routes verbatim n/a — refused before GitHub sees it
P2a POST /repos/{o}/{r}/pulls/18259/comments commit_id + path + line: 49 + side: RIGHT 201 review comment id: 4012020890, user.login: claude[bot] thread appears in ccr/review_threads
P2b GET .../pulls/18259/ccr/review_threads GET 200 [{"resolved":false,"outdated":false,"path":"…rest-channel.md","line":49,"comment_ids":[4012020890]}]
P2c POST .../ccr/comments/4012020890/resolve POST {} 200 {"comment_ids":[4012020890],"resolved":true} review_threads"resolved":true
P2d POST .../ccr/comments/4012020890/unresolve POST {} 200 {"comment_ids":[4012020890],"resolved":false} review_threads"resolved":false
P2e POST .../ccr/comments/4012020890/resolve POST {} 200 {"comment_ids":[4012020890],"resolved":true} review_threads"resolved":true (final state)
P3a PUT .../pulls/18259/ccr/auto_merge PUT {"merge_method":"SQUASH"}, PR in DRAFT 422 {"message":"Pull request Pull request is a draft"} GET /pulls/18259auto_merge: null (nothing stored)
P3b DELETE .../pulls/18259/ccr/auto_merge DELETE, nothing armed 422 {"message":"Can't disable auto-merge for this pull request."} GET /pulls/18259auto_merge: null
P4a POST .../pulls/18259/ccr/ready_for_review POST {} 200 {"draft":false} GET /pulls/18259draft: false; timeline ready_for_review, actor claude[bot]
P4b POST .../pulls/18259/ccr/convert_to_draft POST {} 200 {"draft":true} GET /pulls/18259draft: true; timeline convert_to_draft, actor claude[bot]

P1's route list, quoted from the 403 body itself rather than from the card: GET /repos/{owner}/{repo}/pulls/{n}/ccr/review_threads, POST /repos/{owner}/{repo}/pulls/{n}/ccr/comments/{comment_id}/resolve (or /unresolve), PUT
or DELETE /repos/{owner}/{repo}/pulls/{n}/ccr/auto_merge, POST /repos/{owner}/{repo}/pulls/{n}/ccr/ready_for_review, POST /repos/{owner}/{repo}/pulls/{n}/ccr/convert_to_draft. It matches the card's quotation
word for word.

What the probes settled, against the dispatch's assumptions

  • P2 (which id does resolve want?) — the review-COMMENT id. ccr/review_threads
    returns no thread id at all: each element carries resolved, outdated, path, line
    and comment_ids, and comment_ids[0] is exactly what the resolve route accepts. The
    register row names which.
  • P3 (auto-merge on a draft) — the PUT is REFUSED, and the refusal is the reading: 422,
    nothing stored, auto_merge still null on read-back. The DELETE then also answers 422
    ("Can't disable auto-merge for this pull request") because nothing was armed — so a 422
    from DELETE is not evidence of a failed disarm, and the read-back is what decides.
  • DELETE on an ARMED PR stays unmeasured here, deliberately. Arming auto-merge would
    require a non-draft PR, and this PR's diff touches a governed surface, where Prime
    Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 forbids arming outright. Recorded as not measured rather than inferred.
  • convert_to_draft was already written as a fact on :48 under a tick it had never
    earned; the probe makes the tick true. No new row is owed for it, so none is added.

Rows landed — five, and what paid for each

The file is at its 82-line ceiling with no standing raise exception, so the currency is
deleted content, never a re-wrap. Line count 82 → 82; bytes 6,738 → 6,965.

Row added (bytes) Paid by
- 两只桶:MCP 记链接用户 5000/时… (102 B) tick-convention row + the no-tick prohibition, 108+56 B over 2 lines → 114 B over 1: 不是全局事实 restated 按席位类别限定, and 的形状 / 复述 / 一个 went with it
- ✓ 线程 GET …/ccr/review_threads… (115 B) check-runs/actions row + quota-read row, 88+61 → 120 over 1: 端点自身 and 自读 deleted
- 线程自己建:POST …/pulls/{n}/comments… (109 B) update-branch row + its rationale tail, 91+81 → 114 over 1: the 它是…手段 framing deleted, and 不重写历史 is entailed by 真合并提交
- ✓ POST …/ccr/comments/{id}/resolve… (114 B) the two expected_head_sha rows, 70+85 → 112 over 1: the pinned error prose no new commits on the base branch deleted — nothing parses it, and the standing rule is not to pin error copy
- ⛔ PUT …/ccr/auto_merge 在 draft 上 422… (112 B) bare-PATCH-draft row + the read-back rule, 97+113 → 115 over 1: timeline 的 ready_for_review restated the read-side timeline row

A sixth deletion came first, in its own commit: the two provenance dates left on the
bare-PATCH row and the MCP-fallback row (19 B and 33 B). They are the class the file's own
rules-only lowering removed; deleting them is what let the rows above fit.

No rule was dropped. Every merged row keeps both of its rules; what left the file is
restatement, framing, an entailment and one pinned error string.

Gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths,
derived 15 families on head 95065f36; all 15 run in the foreground with $? captured by
redirect before any pipe; reconciled with --ran:

Run reconciliation — 15 derived, 15 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 15 derived famil(ies) accounted for — 15 run, 0 NOT-MEASURED
  (a DERIVED zero — all 15 recorded an exit code and none of them is 3).

One family first answered exit 3 (PREREQUISITE NOT MET — @objectstack/formula and
@objectstack/lint not built), which is not a red gate; after
turbo run build --filter=@objectstack/formula --filter=@objectstack/lint under the shared
verify lock it exits 0. check:pm-dispatch-gates is not in this card's derivation.

Reverse verification on the head:

  • wc -l82, equal to the ceiling.
  • awk 'length($0)>120' → prints nothing, for touched and untouched lines alike.
  • pnpm check:pm-skill-ratchet → 0 · pnpm check:pm-skill-id-lint → 0 (27 files clean,
    pattern /#[0-9]{3,}/g) · pnpm check:pm-governed-prose → 0.

pnpm lint is CI-owned and was narrowed, with the narrowing proved rather than assumed:
every files: selector in eslint.config.mjs is a JS/TS extension glob and none names
.md; eslint --no-inline-config --format json over the one changed file reports
File ignored because no matching configuration was supplied, 0 errors and 0 warnings from
rules; and since the diff is a single markdown file outside that population, no untouched
file's verdict can move.

skip-changeset: .claude/** is on the fast track — nothing any package's files[] ships
moves here.

维护者速读(草稿)

改了什么 —— PM 席位的 REST 通道对照表新增五条读数:评审线程的三条 CCR 路由(取线程、
解决、取消解决)、auto-merge 在 draft 上的两个拒绝,以及 MCP 与 REST 分属两只限流桶这一事实。

为什么改 —— 会话代理自己的 403 报文点名了这批路由,但表里此前只有其中两条被实调过。
未实调的路由写进表里就是「未带 ✓ 的形状当已验证事实」,而这正是该表第一节明令禁止的。
这一轮每条路由都在本 PR 自己的 draft 上跑过一次才落行。两只桶那条解决的是另一件事:
席位的 MCP 状态动作会被同一 GitHub 用户下别的会话的读耗光,而 CCR 路由走的是另一只桶,
所以「换通道」在这里是合法退路,不是违规续写。

风险与代价(含回滚) —— 只改一个 markdown 参考表,不改任何运行时代码,发布面零变化。
文件行数仍是 82(天花板),字节数 +227。回滚 = revert 本 PR,无迁移、无残留状态。
探针在本 PR 上留下一条评审评论线程(已 resolved)与两次 draft 翻转,PR 终态是 draft、
auto-merge 未挂载。

席位意见 ——

你要做的 —— 无需维护者动作:本 PR 的受管路径全部落在 references/**,按分层裁定
走席位 contract-tier 复审后进队列。

Acceptance notes

  • noted, not filed: added_to_merge_queue now appears three times in this one file (the
    read-side timeline row, the write-side queue-read row, and queue criterion ②), and the
    write-side row still spells git rev-list --parents although :66 routes that spelling to
    platform-readings.md — against the file's own ⛔ 不在两处各存一份. Consolidating it
    would free two more lines. Carrier: the next PR that pays density on this file — the
    rules-layer change already queued behind this one touches :53 in the same block.
  • noted, not filed: DELETE .../ccr/auto_merge on an ARMED pull request has no reading in
    this repo yet, and cannot get one from a governed-surface PR. It needs a non-governed
    card that legitimately arms auto-merge and disarms it again. Carrier: none today.
  • The probe thread on this PR is left RESOLVED. The PR ends draft, auto_merge: null.

Generated by Claude Code

…ft/auto-merge rows

The file's standard is rules-only: provenance dates, ruling citations and
incident narrative left the corpus when it was lowered to its current
ceiling. Two dates survived on the bare-PATCH row and the MCP-fallback row;
deleting them is the currency that pays for the CCR rows this branch adds.

Claude-Session: https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xs documentation Improvements or additions to documentation labels Sep 15, 2026
Comment thread .claude/skills/pm-dispatch/references/rest-channel.md Outdated
@claude
claude Bot marked this pull request as ready for review September 15, 2026 04:49
@claude
claude Bot marked this pull request as draft September 15, 2026 04:49
…uto-merge readings, and the two rate-limit buckets

Five rows land, each paid by deleting content in the same file so the 82-line
ceiling is untouched:

  + the two rate-limit buckets (MCP on the linked user, REST/CCR on the App
    installation) -- paid by folding the no-tick prohibition into the tick
    convention row, whose "not a global fact" clause restated its own first half
  + GET ccr/review_threads and its response shape (no thread id) -- paid by
    folding the quota-read row into the check-runs/actions row
  + the review-comment prerequisite and the resolve/unresolve pair -- paid by
    folding the update-branch rationale tail and the two expected_head_sha rows,
    dropping the pinned error prose nobody parses
  + the two auto_merge refusals (PUT on a draft, DELETE with nothing armed)
    -- paid by folding the read-back rule into the bare-PATCH row, whose
    timeline half restated the read-side timeline row

Claude-Session: https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr
Co-authored-by: Claude <noreply@anthropic.com>
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 15, 2026
@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 95065f3640bf568f76fee1a39efd6b188a9ba3be

Reviewing seat: domain:skills, in-seat review at the contract-review tier — get_session read at 2026-09-15T04:30Z: configured_model = session_context.model = last_served_model, the constant's tier, no fallback notice this round. Card #18130, claim 5674834111, report 5675103223; the PR declares Clause-②: no and the card's claim declares the same.

① Derived judgments

  • Accept set / public surface: none moves — .claude/skills/pm-dispatch/references/rest-channel.md only (+11/−11 against merge-base 917b87ef; no origin/main commit since that base touches the file); references tier, nothing published; skip-changeset on the PR (read back with documentation, size/s).
  • Measurement-first honoured: every CCR route the proxy's 403 body names that the register had never exercised was invoked once on the dev's OWN draft PR and read back — GET …/ccr/review_threads (shape {resolved, outdated, path, line, comment_ids}, no thread id), POST …/ccr/comments/{id}/resolve / /unresolve keyed by the review-COMMENT id (P2 answered), PUT …/ccr/auto_merge on a draft → 422 with nothing stored, DELETE with nothing armed → 422 (a new reading the dispatch did not anticipate: a DELETE 422 is not a failed disarm, only the read-back decides), and the ready_for_review / convert_to_draft pair (the existing convert_to_draft tick, written before any seat had called it, is now earned). The card's readings 2 and 3 were already on :48 / :51 and are not re-recorded; the shared MCP user bucket vs the App installation bucket is written from the card's own measured numbers with no MCP call made.
  • Rows, seat-read on the head: five added (102 / 115 / 109 / 114 / 112 B) and paid by folds inside the same file — the ✓ convention with its no-tick prohibition (2 → 1 line, the prohibition kept), check-runs + actions + /rate_limit (2 → 1, 120 B, every endpoint kept), update-branch + its rationale (2 → 1; 「真合并提交」 kept, 「不重写历史」 entailed), the two expected_head_sha rows (2 → 1, both facts kept), the bare-PATCH draft row + the read-back rule (2 → 1; the timeline ready_for_review reading still lives on the read-side row :27), and two provenance dates dropped (the class the file's rules-only lowering already removed). 82 in, 82 out; awk length prints no line over 120 B; check-skill-line-ratchet ✓, check:pm-skill-id-lint 27 files clean ✓, check-governed-prose ✓ on the head; check-clause2-carriers --pair 18259 exit 0.
  • Gates 15 / 15 / 0 / 0 with exits captured by redirect (doc-formula-expressions after its prerequisite build under the verify lock); pnpm lint narrowed with the three pieces of evidence (population from eslint.config.mjs, the one file reported ignored, invariance stated).

② Semver level

Not applicable — nothing published moves; skip-changeset measured (.claude/** is on the fast path).

③ Boundary flags

open_questions: none. Four declared deviations, each answered: (1) one PATCH /pulls/18259 body write beyond the listed budget — the probe log cannot exist before the probes; sent with NO footer per platform-readings.md :334–:335 and read back as a byte prefix plus the platform's 58 B bare footer, one footer total; accepted. (2) the ready_for_review probe flipped a GOVERNED draft to ready for two seconds — nothing was armed (the auto-merge PUT had already returned 422), the PR was red/blocked throughout and convert_to_draft restored it at once. The seat ORDERED that pair in the dispatch, so the slip is the seat's, not the dev's: AGENTS.md Prime Directive #14 forbids flipping a governed draft to ready, and a measurement is not an exemption. Recorded here against the seat; the seat will not order it again — a ready_for_review reading on a governed PR is the landing act itself, never a probe. (3) DELETE …/ccr/auto_merge on an ARMED PR is NOT MEASURED because arming one on a governed diff is forbidden — recorded as unmeasured, correct. (4) two origin/main merges on the branch for dispatch-gates.mjs's stale-tree refusal; accepted. Landing: references tier ⇒ in-seat contract-tier review → ready → queue once CI on the head is green (Lint & Repo Gates in progress at the record's instant). Out of scope, recorded: added_to_merge_queue spelled three times in this file and git rev-list --parents still on the write-side row against 「不在两处各存一份」 — carried by the next density payment on this file (#18218 touches the same block); DELETE on an ARMED PR needs a non-governed card that legitimately arms and disarms — no carrier today; the unearned-tick class is what measurement-first exists to catch, closed by this PR for convert_to_draft.

Implemented-by: claude/issue-18130-ccr-routes-probed-into-the-register
Reviewed-by: session_01HZfg2AwVX191qCizp88gQr

VERDICT: PASS


Generated by Claude Code

@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Landing provenance — skills seat, session session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T05:34Z. In-seat landing (references tier: .claude/skills/pm-dispatch/references/rest-channel.md only; Governed Surface Queue Guard success on the head): review of record 5675131601 (## Contract review PASS) names head 95065f3640bf568f76fee1a39efd6b188a9ba3be; ACCEPT 5675131811 on #18130; check-clause2-carriers --pair 18259 exit 0 re-read at 2026-09-15T05:31Z with the C6-RECORD note naming that record; on that head Lint & Repo Gates (concluded 2026-09-15T05:31Z) and TypeScript Type Check (concluded 2026-09-15T05:11Z) both success and every other check is success or skipped (37 runs: 22 success, 15 skipped, 0 running, 0 failed); mergeable_state clean. Ready through POST …/ccr/ready_for_review (200, read back draft: false), auto-merge armed through PUT …/ccr/auto_merge with SQUASH (200; the echo and the stored merge_method read merge, the register's known platform fact at platform-readings.md :51–:52 — the queue squashes regardless, as PRs #18251 / #18255 / #18257 landed single-parent today); timeline reads ready_for_review 2026-09-15T05:31Z, auto_merge_enabled 2026-09-15T05:31Z and added_to_merge_queue 2026-09-15T05:32Z — one minute after the arm this time, where the three earlier landings enqueued within seconds; queue ref gh-readonly-queue/main/pr-18259-* present on origin at 2026-09-15T05:33Z. MERGED is confirmed later by two readings (the queue ref gone + git log origin/main carrying (#18259) with one parent), then the card's residue is stripped.


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant