Repository navigation
[finding] platform-readings.md misses the measured facts of the 2026-08-16 quota/credential incident — "reads go REST" is not executable from an MCP-only session; mid-session credential rotation kills the GitHub MCP server #8953
Description
Activity
Triage (skills-lane self-triage, first-touch grading): promoted — measured facts from a real incident, named landing spot (
platform-readings.md), no open question. Grading and claim collapsed into one label write (finding→pm:dispatched). This dispatch also lands #9031's fact row (same facts-table sections); the PR will carryFixes #8953andFixes #9031.Claim: PM loop round 1 (skills seat, maintainer-present session)
Session:session_01Rn7aaamsR99FXRqLcpL99q
Branch:claude/issue-8953-platform-readings-incident-facts
Worktree:objectstack-issue-8953
Domain:domain:skills
File surface:.claude/skills/pm-dispatch/references/platform-readings.md(stop on breach; explain in the report)
Container & model: M,mode:subagent,model: fable— dispatch-gates output this run: "Model tier — MANDATORY: claude-fable-5 (derived from the file surface, not recalled) ⇢ '.claude/skills/pm-dispatch/**' — clause ①"
Clause-②: no
Serial constraints cleared: no open PR and no live claim touchesplatform-readings.md; sibling card #8952 shares this file and is deliberately held out of this batch (follow-up dispatch after this one lands); #9038 stays open — only its addendum fact (write-layer angle-bracket loss) is consumed here as reconciliation input for #9031's row.
Generated by Claude Code
{ "issue": 8953, "status": "done", "branch": "claude/issue-8953-platform-readings-incident-facts", "pr": "https://github.com/objectstack-ai/objectstack/pull/9485", "premise_still_valid": true, "bundled_issue": 9031, "files_changed": [".claude/skills/pm-dispatch/references/platform-readings.md"], "line_budget": "134/134 (ceiling from scripts/pm/check-skill-line-ratchet.mjs; was 133; new facts paid by in-place compression, no re-wrap-only savings)", "gates": { "head": "dbe1c8e13", "pnpm check:doc-authoring": "pass", "pnpm check:pm-skill-id-lint": "pass", "pnpm check:pm-skill-ratchet": "pass (134/134)", "pnpm check:skill-frame-sync": "pass", "pnpm --filter @objectstack/lint run check:doc-formula-expressions": "pass (after building the lint dependency closure)", "node scripts/check-nul-bytes.mjs": "pass", "node scripts/check-adr-merge-approval.mjs": "not locally runnable (direct REST answers 401 Bad credentials in this container); CI-owned; expected red on this PR until a maintainer approval exists, by design", "rederivation": "node scripts/pm/dispatch-gates.mjs on the actual diff returned exactly the six dispatched families; no missed family" }, "deviations": [ "Parked candidate 'auto-merge first arm can silently not enqueue; flip after churn heals' was NOT added as a new row - already covered by the existing enable-verification-sequence row; made explicit there at zero line cost. Candidate 'REST PR files endpoint can transiently 404 (git-first)' was missing and was merged into the git-first row.", "One fact fragment dropped for line budget and recorded in the PR body: the egress-proxy rejection string 'sessions are bound to their configured repositories' (the 403 grant string was kept as the diagnostic signature).", "create_pull_request appended a duplicate session-form footer to the PR body; left as-is (known platform write-path behavior, not another actor)." ], "summary": "Both cards' premises verified against origin/main (file, sections and the three adjacent rows named for reconciliation all present). The 2026-08-16 incident facts, the 2026-08-18 famine readings (field_values fan-out, WebFetch degraded path, git transport) and the read-layer/write-layer body-entity reconciliation landed as merged rows in platform-readings.md; read-layer escaping and the WebFetch path were cheaply re-corroborated this session. Draft PR opened, skills surface: awaiting a human merge, no ready flip or auto-merge.", "tests": "Gate union re-run at final head dbe1c8e13 (tree clean): all six runnable gates green as listed in gates; skip-changeset label applied via read-modify-write union and read back as ['size/m','skip-changeset'].", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
ACCEPT — PR #9485 (lands this card and #9031), reviewer of record: skills seat, session
session_01Rn7aaamsR99FXRqLcpL99q.Verified against GitHub: single file
.claude/skills/pm-dispatch/references/platform-readings.md, +67/−66, ratchet 134/134 — every new fact paid by in-place compression, no re-wrap-only savings observed in the diff. Spot-checks: ① the read-layer/write-layer reconciliation landed as ONE row pair with the boundary stated (read-side entity escaping is reversible; write-side stripping of short angle-bracket fragments is real storage loss; the two are explicitly not merged into "the API mutates bodies"; entity attribution and&-class marked unmeasured) — exactly the #9038-addendum conflict resolution the dispatch required; ② this session's famine readings landed (field_values fan-out kills minimal-field reads too, with the diagnostic error string; WebFetch public-repo degraded path with its three caveats; git-first extended to PR file reads); ③ both parked seat-post candidates were handled with a documented deviation — merged into existing rows rather than duplicated, at zero line cost. Gates green atdbe1c8e13per report, re-derivation matched the six dispatched families; theADR maintainer approvalred is the human-review gate working as designed.⛔ Accepted, NOT landed — skills root, ADR-class: PR stays draft, awaiting the maintainer. #9031 closes with it (
Fixesboth). Follow-on: #8952 stays held until this PR merges (same file).
Generated by Claude Code
Category
平台事实变化 → one-line rows for
.claude/skills/pm-dispatch/references/platform-readings.md(the third type accepted by the shift-report rule).Measured facts (2026-08-16, remote cloud PM session)
list_issues/search_issuesare GraphQL-backed server-side with no REST alternative, and direct REST is gated by a per-session GitHub grant (403 "GitHub access is not enabled for this session") snapshotted at session start; the egress proxy additionally rejects non-repo-scoped paths (/search/*,/rate_limitexcepted) with"sessions are bound to their configured repositories". A seat that hits a drained GraphQL pool therefore has no list path at all until reset. The facts table currently states the REST rule as if it were a choice the seat can make.mcp__github__*call returnedStreamable HTTP error: invalid session— including tools that had worked minutes earlier. Not recoverable in-session; a fresh session re-binds. Advise the maintainer accordingly before rotating credentials while seats are running: in-flight seats lose their GitHub channel, not just their quota state. Corollary: quota pools are per-identity, so an identity switch also resets the burn — but shared-identity structure is unchanged.search_issuesfans out per-issue GraphQL calls for Projectfield_values(observed failure:failed to fetch issue field values). Omitfield_valuesvia thefieldsparameter in every list/search call; no pm code path reads it.open_issues_countbefore believing it.304 Not Modifieddo not count against the core pool — relevant only if a direct-REST read path is ever authorized for seats.Proposed change
Add the facts above as rows in the API-quota / MCP-traps sections of
platform-readings.md. Adjacent existing rows to reconcile, not duplicate: thelist_issuesno-assignees row, the multi-label-OR row, and the HTML-escaped-body row.Constraints
Fix lands in
.claude/skills/pm-dispatch/references/**→domain:skills;claude-fable-5mandatory; human review + human merge (skills are ADR-class).Provenance
Filed at the maintainer's direction in the 2026-08-16 PM session (「给 skills 席位写 issue」). Sibling card filed in the same batch: pre-round API-quota gate for pm-dispatch.
Generated by Claude Code