Skip to content

console(oauth consent): the metadata:write scope from #26550 is shown to the user as the raw string, not as a sentence of what they are granting #26587

Closed

Description

@objectstack-fleet

Filing gate ①: a product defect with a named location, owed by work in flight. Found by the #26464 os-dev report (6437984964, out_of_scope item 1, code read, not browser-measured) and confirmed by domain:spec seat 1 (#6017, session session_01WGufzfhobWEgUUrjz4rrbh) reading origin/main. ⛔ Not a claim.

Blocked-by: #26550

Business reason: PR #26550 adds the OAuth scope metadata:write, which lets an MCP client (Claude Code, Claude Desktop, …) change an environment's metadata. Once it lands, the authorization server advertises it and a client can ask for it. The person who approves that request on the Console consent screen sees the bare string metadata:write beside three plain sentences for the other agent scopes. The one scope that lets an agent build in their environment is the only one the screen does not explain.

Reader who acts: the lane triage names (ui/apps/console).

Where

At origin/main, read 2026-10-27:

  • ui/apps/console/src/pages/auth/OAuthConsentPage.tsx:274-313, describeScope: cases for openid, profile, email, offline_access, data:read, data:write and actions:execute. No case for metadata:write, so default: return scope renders it raw (:239).
  • PR feat(mcp): bridge a host-injected authoring ToolRegistry over HTTP behind a metadata:write OAuth scope #26550 declares MCP_OAUTH_SCOPE_CONSENT_COPY in packages/spec/src/ai/mcp.zod.ts (:170 on its head): one consent sentence per McpOauthScope, keyed by the scope so a scope cannot be added without one. The three existing sentences there are the ones the Console hard-codes today.

Wanted

The Console reads the agent-scope sentences from MCP_OAUTH_SCOPE_CONSENT_COPY (as the defaultValue behind its translation keys, or directly), so metadata:write gets its sentence and a future scope cannot render raw. Add a translation key for the new sentence in every bundle. A test pins that every MCP_OAUTH_SCOPES member renders a sentence and none renders its raw string.

Also carried here (prose drift below the filing bar, same report item 2)

docs/qa/platform-checklist/areas/ai.json:186 says a token with none of the MCP scopes is refused with insufficient_scope "naming all three scopes (data:read, data:write, actions:execute)". After #26550 there are four. Update the clause when the Console half lands, or in #26550's follow-up if that comes first.

Dedupe: open issues and PRs grepped for describeScope, OAuthConsentPage and metadata:write consent: none besides #26464 / #26550 themselves, which leave the Console out of scope.

Clause-②: no
Metadata-protocol: none


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    on Oct 27, 2026
  2. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · security · priority:p1 (from p2) · domain:ui · area:access · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T09:54Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01QsR2jTfvjNTaR29ehLk26T
    Account: zhuangjianguo
    Branch: claude/project-thread-9l8r78 (platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)
    Worktree: objectstack-issue-26587
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: ui/apps/console/src/pages/auth/OAuthConsentPage.tsx and its test, ui/packages/i18n/src/locales/*.ts (one new consent key), the scope-count clause in docs/qa/platform-checklist/areas/ai.json (devx file, carried by this card), one root changeset (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate; reason: the maintainer's standing rule 「只认领不需要 fable 的卡」, 2026-10-27T10:39Z), effort: xhigh
    Clause-②: yes
    Metadata-protocol: none
    Responsibility: OAuthConsentPage.describeScope (Console, this lane) renders an unknown scope raw · the platform path that covers it: MCP_OAUTH_SCOPE_CONSENT_COPY in @objectstack/spec (#26550), not read by the Console · reached by every user approving an MCP client that asks for metadata:write, live since #26550 merged
    Thread-read: 6438780502
    Premise: holds (action, card-reference and work-item surfaces re-read on origin/main 8c0306f5 at 2026-10-27T10:50Z)
    Serial constraints cleared: draft #26365 (held, plugin-ai retirement) also edits ui/packages/i18n/src/locales/en.ts; whichever lands second merges main. No other open PR or in-flight claim on these files (scan 2026-10-27T10:50Z)

    Dispatched at 2026-10-27T10:53Z to the card thread on that branch; the order lives in the thread's opening message.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 26587,
    "status": "done",
    "branch": "claude/project-thread-9l8r78",
    "pr": "#26659",
    "session": "session_01QsR2jUZATdbPBDkvjTyX8D",
    "head_sha": "3f2b32c8",
    "premise_still_valid": true,
    "premise_readings": "(a) describeScope at origin/main had no metadata:write case and fell to default: return scope — holds. (b) @objectstack/spec/ai exports MCP_OAUTH_SCOPES and MCP_OAUTH_SCOPE_CONSENT_COPY (packages/spec/src/ai/mcp.zod.ts; api-surface/ai.json) and the console already depends on @objectstack/spec — holds. (c) 9bf6598 is an ancestor of origin/main: merge-base --is-ancestor exit 0 (a positive reading, no control leg owed). ai.json still said three scopes at c739de7 — holds.",
    "summary": "OAuthConsentPage routes every MCP_OAUTH_SCOPES member to describeAgentScope, a switch exhaustive over McpOauthScope (no default) whose cases read their pack key with MCP_OAUTH_SCOPE_CONSENT_COPY[scope] as defaultValue; metadata:write now renders its sentence. The default branch renders oauth.consent.scope.unrecognised ('Unrecognised permission: ' plus the scope, through a scope placeholder) instead of the bare scope. All ten locale packs gain metadataWrite and unrecognised. ai.json ai.mcp-http-surface revision 6: acceptance[5] clause + verify name four scopes, steps[5] says four, variants gains the metadata:write row, history row added. One root changeset, patch for @object-ui/i18n and @object-ui/console.",
    "declarations": {
    "Clause-②": "yes (copied from claim 6440101598)",
    "Metadata-protocol": "none (copied)",
    "changeset": ".changeset/26587-consent-metadata-write.md — @object-ui/i18n patch, @object-ui/console patch"
    },
    "files_changed": [
    ".changeset/26587-consent-metadata-write.md",
    "docs/qa/platform-checklist/areas/ai.json",
    "ui/apps/console/src/pages/auth/OAuthConsentPage.tsx",
    "ui/apps/console/src/pages/auth/tests/OAuthConsentPage.agent-scopes-12853.test.tsx",
    "ui/apps/console/src/pages/auth/tests/OAuthConsentPage.mcp-scopes-26587.test.tsx",
    "ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts"
    ],
    "line_budget": "15 files, +229 / -32 = 261 changed lines (git diff --numstat origin/main...HEAD; dispatch-gates: 261 vs the 3000 human-merge threshold: under). Claim size M; no numeric budget was named.",
    "tests": "At 3f2b32c, under os-verify-lock: (1) cd ui && pnpm exec vitest run the three OAuthConsentPage test files --maxWorkers=1 — Test Files 3 passed (3), Tests 23 passed (23). (2) cd ui && pnpm exec vitest run packages/i18n/ --maxWorkers=1 — Test Files 89 passed (89), Tests 1373 passed | 13 skipped. (3) pnpm exec turbo run type-check --filter=@object-ui/i18n --filter=@object-ui/console --concurrency=2 — Tasks 71 successful, 71 total, 0 cached, VERDICT command-exit 0 (type-check covers both packages' tests). Ablations via scripts/ablation-replace.mjs (vitest resolves the ui packages from src, no dist leg): A drop zh metadataWrite — anchor 1 -> 0, blob 4a8ff35cd28f -> 0d88d403f28e, Tests 1 failed | 11 passed (12) (the zh case), restored blob == HEAD, git diff HEAD empty; B send metadata:write past the agent-scope routing — anchor 1 -> 0, Tests 10 failed | 2 passed (12), restored blob == HEAD (935b2c4ded21), git diff HEAD empty.",
    "gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 41 at 3f2b32c; each run with exit captured before any pipe: 41/41 exit 0. --ran reconciliation: '41 derived famil(ies) accounted for — 41 run, 0 NOT-MEASURED (a DERIVED zero — all 41 recorded an exit code and none of them is 3)'. PR-body gates against the exact body sent (GITHUB_EVENT_PATH / PR_BODY simulation): check-changeset-no-major exit 0, check-adr-0087-registration exit 0, check-closing-keyword-parity exit 0, check-partof-closing-keyword exit 0, check-closing-target-claim exit 0 with an UNDETERMINED warning (see deviations), check:changeset-fixed exit 0. ui gates outside the derivation: check-i18n-call-site-keys exit 0, check-i18n-en-drift exit 0, check-new-cross-file-line-citations exit 0 (0 new). CI families (Type Check UI, Test UI, Build & E2E UI, Lint UI, performance budget): in_progress on the PR, not re-run locally.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "deviations": [
    "Claim file surface says 'one new consent key'; the packs gain two (metadataWrite and unrecognised), because the triage ruling and the order's route ask for a translated unrecognised line.",
    "ai.json: besides acceptance[5] (the clause the card names), its verify string, steps[5] ('NONE of the three MCP scopes') and the variants matrix (new metadata:write row) were stale for the same reason and are corrected in the same revision 6.",
    "check-closing-target-claim prints UNDETERMINED for #26587: the newest Claim's Branch line carries a parenthetical after the branch name and the gate cannot parse it (exit 0, warning only). The seat may want to restate the Branch line bare.",
    "Eager-bundle cost of importing @objectstack/spec/ai into the eagerly loaded OAuthConsentPage was not measured locally; the constants sit in a spec chunk that @objectstack/spec/system also reaches. Left to CI's performance budget.",
    "The harness attribution reminder asked for a model-named Co-Authored-By trailer; commits carry the model-free pair per AGENTS.md."
    ],
    "mcp_calls": "0 — no MCP GitHub calls",
    "api_writes": "2 — relay dispatch POST /repos/objectstack-ai/objectstack/dispatches for pr_create (run 40499068186, read-back identical, 10263 bytes, assignees zhuangjianguo); post-stamped comment POST /repos//issues/26587/comments (this report). Plus git push (not REST).",
    "open_questions": [],
    "out_of_scope_findings": []
    }


    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

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:uipm:dispatchedpriority:p1High: required for production / M2security

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions