Skip to content

Commit a99de44

Browse files
docs(adr): ADR-0090 D10 rule 5, the agent forward credential (step 1 of #28367) (#28455)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} --> _Requested by **Tesla** · [project thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoEY1cKkXmjt2KhuP1QWGYsM)_ Part of #28367. This is step 1 of the ruling's order, the ADR revision. The code PR is step 2, a separate change made after this lands, so the card stays open. Clause-②: yes (widening, at the named cells of the #28178 B refusal table only) Metadata-protocol: none ## 维护者速读 - 改了什么:ADR-0090 D10 新增第 5 条 agent 规则「转发带的是 agent,不是用户」:agent 运行 `api` 动作时,进程内转发改带一张服务端自己签发的转发凭据,只对这一次转发有效(一个方法、一个路由、一次动作运行),一次性、短时、不可重放、出不了进程;到目标端解析成同一个 agent 主体(scope 派生的权限、onBehalfOf、performedBy)。绝不带调用方的 token 或 API key,也绝不签发人类会话(C 方案继续拒绝)。本 PR 只改 ADR,不写代码。 - 为什么改:Tesla 在第 434 批第 4 项答「同意」(裁决记录 6545950368,go as shaped)。今天 OAuth 连接的 agent 跑任何转发到身份路由或平台路由的 `api` 动作都会按 #28178 B 表被点名拒绝,所以 ADR-0011 已开放给 agent 的身份动作实际一个都跑不通;裁决要求先在 ADR 里逐格写明哪些格子翻转,再写代码。 - 风险与回滚:放宽只落在两格:OAuth agent × 身份路由、OAuth agent × 平台路由,后者受 scope 族规则约束(只授权读的 agent 通过动作也改不了任何状态,元数据门的写操作不翻转)。API key × 身份路由继续点名拒绝:API key 是主人自己的凭据而非 agent 主体(D10 2026-09-16 注记),给它开等于换个名字的人类会话。外部 OAuth token 在哪些门有效不变。若认为 API key × 身份路由也该翻转,那是另一项裁决。回滚即 revert 本 PR;代码尚未写,今天无运行时变化。 - 先看:`docs/adr/0090-permission-model-v2-concept-convergence.md` 的 D10 末尾新增节「Amendment (2026-10-31, #28367)」中的表格(标 FLIPS 的两格)、"No human session" 段(身份路由处 better-auth 只看到委托用户的请求级视图)、scope 族规则四条。 - 两分钟验证:打开该文件搜 `FLIPS`,预期恰好两格翻转,`api_key` 行身份列仍为 refuse by name。 - 需要你做的:以 os-zhuang 身份审批本 PR(Tier H,席位会发起评审请求)。 ## What changes One file, `docs/adr/0090-permission-model-v2-concept-convergence.md`. No code and no `packages/**`. - **Status block:** one dated `Amended (2026-10-31, ruling of record 6545950368 on #28367)` line. - **TL;DR D10 bullet:** one sentence pointing to rule 5. - **D10's agent bullet:** "Four defining rules" becomes five. Rule 5 is a short statement pointing to the amendment. - **The amendment** (a dated subsection at the end of D10, after the 2026-09-20 status note): - **The credential.** Four binding properties: the server mints it after the action door's own gates pass; it is bound to one method, one route and one action run in the consent's organization; it is single-use, short-lived and non-replayable; it never leaves the process and is never stored, logged, relayed or audited. - **What it resolves to.** The same agent principal, built by `entryFields` from handed-over provenance: the scope-derived `permissions`, the existing `systemPermissions` rule, `onBehalfOf`, `performedBy`, `oauthScopes` and the consent's organization. - **No human session.** At the identity mount, better-auth gets a request-scoped in-memory view of the delegating user for that one request. There is no `sys_session` row, token or cookie. Two gates stand: the action door's gate (the agent's) and better-auth's own (the user's). Identity writes are attributed to the agent and the user. Credential-rotation headers are never relayed to an agent caller. - **The table.** The #28178 B table is reproduced with the flipped cells marked. Two cells flip: `oauth` × `identity` and `oauth` × `platform`. `api_key` × `identity` keeps refusing by name, with the reason. `oauth` × `mcp` is unchanged. - **The scope-family rule for targets outside MCP.** `actions:execute` is always required. A safe method needs `data:read` or `data:write`, and any other method needs `data:write`. A state-changing method on the metadata door stays refused. Refusals are by name before the mint, and `list_actions` marks the action from the same predicate. - **Where the code half lives.** `packages/runtime` (the action doors and the HTTP dispatcher's resolution), `packages/rest` (the REST face's resolution) and `packages/plugins/plugin-auth` (the identity mount). `packages/core` is unchanged. The amendment also lists the pins that PR owes. - **What does not change**, and the two sentences elsewhere in D10 that the code PR must correct when it lands. ## Acceptance notes - **The API-key cell is the one judgement call.** The card's title names "an OAuth- or API-key-connected agent". The ruling binds the credential to resolve to "the same agent principal". D10's 2026-09-16 note rules that an API key is its owner's credential and not an agent principal. So the amendment keeps `api_key` × `identity` refusing by name and says why. If the maintainer wants that cell flipped too, it is a separate decision: what a key would carry is the owner's own reach. - **The table's code lives on the #28178 PR (#28442, draft at the time of writing).** On `main` the API-key half is `apiActionIdentityRouteKeyRefusal`. The amendment names the table by what it decides and cites only symbols that exist on `main`, so `check:adr-symbol-anchors` stays green whichever lands first. - **The 2026-10-31 agent-door finding on the connection door.** An org admin's agent with `actions:execute` carries the admin's `systemPermissions`. That finding is the shape the scope-family rule addresses for forwards. The rule does not change the connection door itself. - No changeset: `docs/adr/**` publishes nothing, so `skip-changeset` goes on at creation. - Branch `claude/project-thread-hem8y5` is the card thread's harness-pinned branch. Claim 6546502251 names it. ## Verification Local, on `5e899ebd`: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 21 families for the one path. Each ran with stdin from `/dev/null`, and its exit code was captured by redirect before any pipe. <details><summary>Gate readings</summary> - First pass: 13 exited 0. Eight exited 3 (PREREQUISITE NOT MET, no `node_modules` in the fresh worktree). After `pnpm install --frozen-lockfile`, 7 of the 8 exit 0. `check:doc-formula-expressions` exited 3 again because `@objectstack/formula` and `@objectstack/lint` were not built. After `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint` under the verify lock (VERDICT command-exit 0), it exits 0. - Final: all 21 exit 0, including `check:adr-links`, `check:adr-anchors`, `check:adr-index`, `check:adr-symbol-anchors`, `check:doc-authoring`, `check:closing-keyword-parity`, `check:nul-bytes` and `check:pm-governed-merges`. - `dispatch-gates --ran`: `21 derived famil(ies) accounted for — 21 run, 0 NOT-MEASURED`, exit 0. - Outside the derivation: `node scripts/check-role-word.mjs` (ADR-0090 D3 ratchet) exit 0. - `pnpm lint`: no package touched, so there is no scoped lint run. </details> --- _Generated by [Claude Code](https://claude.ai/code/session_01L9qibyagSyLB5ckJsKghoa)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 50015d8 commit a99de44

1 file changed

Lines changed: 190 additions & 2 deletions

File tree

‎docs/adr/0090-permission-model-v2-concept-convergence.md‎

Lines changed: 190 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
· **Amended** (2026-10-15, ruling of record 6181988435 on #23488, letter A) — D12's position↔set binding right and its environment-set authoring right are retired (`AdminScope.manageBindings` and `AdminScope.authorEnvironmentSets`, ADR-0049 enforce-or-remove): a position's sets and a permission set's body are environment definitions, and the definition write is `manage_metadata`'s per [ADR-0131](./0131-total-organization-ownership-no-null-organization-id.md) D3, never a delegate scope's. D12's *what* is user↔position assignments (and direct set grants) inside the subtree; read its bindings and authoring clauses as superseded by this line.
99
· **Amended** (2026-10-14, [ADR-0138](./0138-guest-model-anonymous-principal-doors-grants-and-organization.md) D3 item 5, ruling C of record 6159279205 on #23267) — D9's last bullet holds for `everyone` alone: a package suggests `everyone` bindings and the admin confirms each, while no door binds a set to `guest` (no package suggestion, no confirmation, no operator bind) until ADR-0138 D3 item 5's bind door is built, an act of the environment's administrator, never of a package. Read that bullet's `guest` half as superseded by this line.
1010
· **Amended** (2026-10-17, ruling of record 6228728551 on #23960, letter B) — D12 names three kinds of administrator, tenant-level, organization-level and delegated: an organization administrator (`organization_admin` / `organization_admin_no_bypass` for the organization, not a platform administrator) creates and removes `sys_user_position` / `sys_user_permission_set` rows for that organization's members when every set the row distributes has no `*` wildcard superuser bit, no `adminScope` and only `scope: 'org'` capabilities; `admin_full_access`, platform sets, `organization_admin` itself and every delegated set stay outside, and organization-administrator status is conferred by membership grade only. Strict containment and the `everyone` / `guest` anchors are unchanged. See the note under D12.
11+
· **Amended** (2026-10-31, ruling of record 6545950368 on #28367, go as shaped) — D10 gains rule 5, the **agent forward credential**: an `api` action's in-process forward carries the agent principal in a credential the server mints itself, bound to that one forward, single-use, short-lived and unreachable out of process, never the caller's token or key and never a human session. Two cells of the credential-kind × target-door table flip from "refuse by name" to "accept with a forward credential" (an OAuth-connected agent at an identity route and at a platform route), and a scope-family rule bounds every target outside MCP. See the amendment under D10.
1112
**Deciders**: ObjectStack Protocol Architects
1213
**Builds on**: [ADR-0049](./0049-no-unenforced-security-properties.md) (enforce-or-remove),
1314
[ADR-0056](./0056-permission-model-landing-verification.md) (permission-model landing verification),
@@ -66,7 +67,9 @@ Twelve decisions:
6667
- **D10** — A **principal taxonomy** (`kind: human | agent | service | guest | system`,
6768
`audience: internal | external`, optional `onBehalfOf`) enters ExecutionContext. **AI agents run
6869
on the intersection** of their own grants and their delegator's, under a lint-enforced ceiling,
69-
with human co-sign required for destructive operations.
70+
with human co-sign required for destructive operations. An action's in-process forward carries
71+
the agent principal in a server-minted, single-use forward credential, never the user's
72+
(rule 5, amended 2026-10-31).
7073
- **D11** — OWD gains an **external dimension**: optional `externalSharingModel`, default
7174
`private`, validated `external ≤ internal`. The spec shape lands in P1.
7275
- **D12** — **Delegated administration**: admin capability itself becomes scoped (BU subtree +
@@ -404,7 +407,7 @@ depends on:
404407
no interactive login, no seat, its own audit trail. Lint: no VAMA, no destructive bits without
405408
explicit justification. Exists so customers stop wiring integrations through a human admin
406409
account — the classic audit finding.
407-
- **`agent`** — an AI actor. Four defining rules:
410+
- **`agent`** — an AI actor. Five defining rules (the fifth added 2026-10-31):
408411
1. **Intersection, never union**: acting for a user, effective permission =
409412
*agent's own grants ∩ the delegator's grants* — the confused-deputy prevention that OAuth
410413
on-behalf-of and Kerberos S4U converged on. An over-privileged agent prompted by an
@@ -419,6 +422,11 @@ depends on:
419422
MCP client door alone. A caller of any other kind records the principal it authenticates as,
420423
and the missing `performed_by` is itself the record that the principal acted for itself; for
421424
the API-key case see the 2026-09-16 note at the end of this decision.
425+
5. **A forward carries the agent, never the user** (amended 2026-10-31): when an agent runs an
426+
`api` action, the in-process forward reaches its target with a forward credential the server
427+
mints for that one forward, which resolves there to this same agent principal. The caller's
428+
token or key never travels, and no human session is minted. See the amendment at the end of
429+
this decision.
422430
Task-scoped, time-boxed agent grants build on the grant-lifecycle follow-up ADR (see
423431
*Named follow-ups*).
424432
- **`guest`** — resolves to the `guest` position (D9) and nothing else.
@@ -481,6 +489,186 @@ alias tax on every API.
481489
> a sibling can be added without re-shaping a shipped field; it reserves the room, ⛔ not the name.
482490
> Ruling: director seat, batch #195 item 2, maintainer 「同意」 2026-09-20 — letter **C**.
483491
492+
#### Amendment (2026-10-31, #28367) — the agent forward credential (rule 5) [ruled]
493+
494+
**What this decides.** How an agent principal crosses an `api` action's in-process forward. A
495+
`type: 'api'` action with a same-origin `target` runs by forwarding a request to that target inside
496+
the process (`packages/runtime/src/action-execution.ts#forwardApiAction`), and until now the
497+
forward carried the caller's own credentials (`API_FORWARD_HEADER_ALLOWLIST`) so the target would
498+
authorize the same principal the action door admitted. That works for a person and fails for an
499+
agent. The OAuth access token that makes a caller an agent principal is honoured on the MCP
500+
endpoint alone, and an identity route (the auth mount, `/api/v1/auth/…`) reads its own signed-in
501+
session and nothing else. So the action door could admit an agent the user consented to
502+
(`actions:execute`) for an action the author exposed (ADR-0011 `ai.exposed`, the identity action
503+
buckets of its 2026-10-30 addendum), and the target would then read that agent as nobody.
504+
505+
The #28178 ruling (option B, record 6540286612) made that failure loud: one table, credential
506+
kind × target door, shared by `run_action`, the REST action door and `list_actions`, refuses such
507+
a forward by name before the target is called, and the API-key refusal of #28217
508+
(`packages/runtime/src/action-execution.ts#apiActionIdentityRouteKeyRefusal`) rides the same table.
509+
This amendment is the other branch of that decision: the credential that lets the forward reach
510+
its target with the agent intact. It does not move the agent's ceiling, and it does not hand the
511+
agent the user.
512+
513+
**Rule 5 — a forward carries the agent, never the user.** When the action door forwards an
514+
agent's `api` action, the forward reaches its target with an **agent forward credential**. These
515+
properties are the decision, each of them binding:
516+
517+
1. **The server mints it.** It is never the caller's bearer, cookie or API key. A forward that
518+
carries it carries none of the caller's identity headers, so the caller's token never reaches
519+
the target. It is minted only after the action door's own gates have passed for this run:
520+
exposure (`ai.exposed`), the action's permission against the agent principal, the confirmation
521+
(ADR-0011 `ai.requiresConfirmation`, rule 3's co-sign), and the operator's activation switch
522+
(ADR-0126). A refused run mints nothing.
523+
2. **It is bound to one forward**: one method, one route (the resolved path the forward requests),
524+
one action run (the object, the action and the record it names), in the one organization the
525+
user's consent bound. Presented with any other method or route, it is refused.
526+
3. **It is single-use, short-lived and non-replayable.** It is spent by its first presentation,
527+
whether the target accepts the call or refuses it. It expires when its forward answers, and in
528+
any case after a fixed bound no longer than the forward's own request timeout, which the code
529+
pins. A second presentation is refused.
530+
4. **It is unreachable out of process.** It exists only inside the process that minted it and is
531+
presented only through the in-process injector. A request that arrives over the network
532+
carrying anything that looks like it resolves as if it carried nothing. It is never stored,
533+
never logged, never written to a response body or header, and never recorded in an audit row.
534+
535+
**What it resolves to at the target: the same agent principal.** The target's context is built
536+
by the shared assembler (`packages/core/src/security/assemble-execution-context.ts#entryFields`)
537+
from the provenance the minting door hands over, never re-derived from a token:
538+
`principalKind: 'agent'`; `permissions` = the scope-derived ceiling
539+
(`packages/spec/src/ai/mcp.zod.ts#scopesToAgentPermissionSets`); `systemPermissions` exactly as
540+
this decision already gives an agent (the delegator's under `actions:execute`, none without it);
541+
`onBehalfOf` = the delegating user; `performedBy` = the agent's client; `oauthScopes`; the
542+
consent's organization. Rule 1's intersection, rule 2's ceiling and rule 4's dual attribution
543+
therefore hold at the target as they hold at the action door. No principal kind is added.
544+
545+
**No human session.** Option C of #28178, minting a session for the forward, stays refused: a
546+
session is valid at every identity route, for its whole lifetime, wherever it is presented, so it
547+
would hand the agent the user's whole reach. The identity mount serves its routes through
548+
better-auth, which reads a session and nothing else, so at that door the credential is resolved
549+
before better-auth runs and better-auth is shown the delegating user as its caller for that one
550+
request only: a request-scoped view built in memory, with no `sys_session` row, no token, no
551+
cookie, and nothing that outlives the request or is handed back to anyone. Two gates then stand,
552+
and the agent passes only if both do: the action door's (the agent's intersected authority, the
553+
exposure and the confirmation, checked before the mint) and better-auth's own (the delegating
554+
user's, at the target). That is rule 1's intersection, read across the two doors. The agent and
555+
its user are carried into the identity mount's attribution scope
556+
(`packages/plugins/plugin-auth/src/auth-actor-attribution.ts#runWithAuthActorScope`), so an
557+
identity write records `performed_by` (the agent) and `on_behalf_of` (the user). And the
558+
credential-rotation headers a target can answer (`API_FORWARD_RELAYED_RESPONSE_HEADERS`) are never
559+
relayed to a caller whose forward carried a forward credential: a target that mints a credential
560+
cannot pass one to an agent, whatever the action declares.
561+
562+
**The table, cell by cell.** The table the #28178 ruling set (credential kind × target door,
563+
shared by `run_action`, the REST action door and `list_actions`), with the cells this amendment
564+
flips marked **FLIPS**. Every other cell keeps its answer, and every refusal it keeps is still
565+
given by name, before the forward, with the target never called.
566+
567+
| Caller's credential at the action door | `identity` — the auth mount (`/api/v1/auth/…`) | `mcp` — the MCP endpoint | `platform` — every other same-origin route (data door, declared endpoints, plugin routes) |
568+
|:--|:--|:--|:--|
569+
| `session` (a signed-in person) | accept, the caller's own session (unchanged) | accept (unchanged) | accept (unchanged) |
570+
| `api_key` (its owner's credential) | **refuse by name (unchanged)** | accept (unchanged) | accept (unchanged) |
571+
| `oauth` (an agent principal) | **FLIPS:** refuse by name → accept with a forward credential | accept, the caller's own token (unchanged; no forward credential) | **FLIPS:** refuse by name → accept with a forward credential, under the scope-family rule below |
572+
573+
Two cells flip and one refusal stays:
574+
575+
- **`oauth` × `identity` flips.** This is the cell that keeps the identity actions ADR-0011
576+
exposes out of an agent's reach today.
577+
- **`oauth` × `platform` flips**, bounded by the scope-family rule. Without the rule this cell
578+
would let an action reach a route that checks system permissions only, past the families the
579+
user consented to.
580+
- **`api_key` × `identity` keeps refusing by name.** An API key is its owner's credential, not an
581+
agent principal (the 2026-09-16 note above): the principal a key admits is the owner, a human.
582+
The forward credential is defined to carry an agent principal, so a key has none for it to
583+
carry. A credential the identity mount accepted on a key's behalf would carry the owner's whole
584+
reach, which is a human session under another name: option C. A key caller who needs an
585+
identity action uses a signed-in session, as the existing refusal already tells them.
586+
Reopening this cell is its own decision, ⛔ not a reading of this one.
587+
- **`oauth` × `mcp` is unchanged.** The MCP endpoint honours the caller's own token, so no forward
588+
credential is minted for it.
589+
- An anonymous caller is in no row: the action door's own floor answers it first, and nothing is
590+
minted.
591+
592+
**The scope-family rule, for every target outside MCP.** At the MCP endpoint the consented tool
593+
families (`data:read`, `data:write`, `actions:execute`, `metadata:write`) decide which tools
594+
exist, and the scope-derived ceiling bounds object access. Outside MCP there are no tool families.
595+
A target that gates on system permissions only, with no object check for the ceiling to narrow
596+
(identity routes, plugin administration routes, setup routes), would see an agent's
597+
`systemPermissions`, which under `actions:execute` are the delegator's whole list. So a forward
598+
credential is minted only when the agent holds the family the forward needs:
599+
600+
1. **Every forward credential needs `actions:execute`.** Only the action doors mint it, and they
601+
serve an agent only under that scope.
602+
2. **The method names a data family.** A safe method (`GET`, `HEAD`) needs `data:read` or
603+
`data:write`. Any other method needs `data:write`. An agent the user consented to read with can
604+
read through an action, and can never change state through one, at any target, including a
605+
target that has no object check.
606+
3. **A state-changing method on the metadata door (`/api/v1/meta/…`) is outside the flip** and
607+
keeps refusing by name. An agent authors metadata through the MCP authoring tools under
608+
`metadata:write`, which save drafts that a person publishes (cloud ADR-0112), never through an
609+
action forward.
610+
4. A forward the rule refuses is refused by name before the mint, the same way the table's other
611+
refusals are, and `list_actions` marks the action from the same predicate, so the listing and
612+
the run door cannot disagree.
613+
614+
**Where the code half lives, and what it pins.** None of it is written by this amendment; the
615+
code PR is a separate change on #28367, made after this amendment lands.
616+
617+
- `packages/runtime`, the action doors (`packages/runtime/src/action-execution.ts#forwardApiAction`
618+
and the table beside it): the mint after the gates, bound to the built request; the two flipped
619+
cells and the scope-family predicate on the one table, read by `run_action`, the REST action
620+
door and `list_actions` alike; the in-process registry that spends and expires each credential.
621+
- `packages/runtime`, the HTTP dispatcher's context resolution
622+
(`packages/runtime/src/security/resolve-execution-context.ts`): a forward credential presented
623+
through the in-process injector resolves to the agent provenance it was minted with.
624+
- `packages/rest`, the REST face's context resolution (`packages/rest/src/rest-server.ts`, which
625+
hands the assembler `oauth: undefined` by construction today): it accepts the forward
626+
credential, and nothing else, as agent provenance. An OAuth access token presented from outside
627+
stays unaccepted there.
628+
- `packages/plugins/plugin-auth`, the identity mount
629+
(`packages/plugins/plugin-auth/src/auth-manager-request-routing.ts#handleRequest`): the
630+
resolution before better-auth runs, the request-scoped view, the agent's attribution, and no
631+
credential header relayed to an agent caller.
632+
- `packages/core` is unchanged: the provenance passes through `entryFields`, so the agent envelope
633+
is still built in one place.
634+
635+
The code PR owes these pins, and each refusal pin is ablated (the forbidden behaviour put back,
636+
seen red, restored):
637+
638+
- each flipped cell, positively, driven by a real OAuth-connected agent rather than a session:
639+
an exposed identity action and a platform action are served, and the audit row records both
640+
`performed_by` and `on_behalf_of`;
641+
- every refusal the table keeps still refuses by name: `api_key` × `identity`, a state-changing
642+
method on the metadata door, and each scope-family refusal (a `data:read`-only agent with a
643+
state-changing method, at an identity target and at a platform target);
644+
- the credential's properties: a second presentation refused; a different method or route
645+
refused; an expired one refused; one presented from outside the process resolving as nobody;
646+
none in any response header, log line or audit row;
647+
- the intersection at the target: an agent whose user lacks the target's permission is refused
648+
by the target;
649+
- no session: a forward creates no `sys_session` row, and a credential-rotating target answers
650+
no credential header to the agent;
651+
- the boundary, unmoved: an outside OAuth access token is still refused at the data door and at
652+
the identity mount;
653+
- the mint and each acceptance seam carry this decision's id in the code, with an anchor in
654+
`scripts/adr-anchors/`.
655+
656+
**What does not change.** Where an outside caller's OAuth access token is honoured (the MCP
657+
endpoint alone); which credentials an identity route accepts from outside the process; the API-key
658+
reading of the 2026-09-16 note; rules 1 to 4; ADR-0011's exposure, buckets and confirmation; the
659+
principal kinds. The forward credential is bound to one action run, but it is not the run id the
660+
2026-09-20 status awaits: it lives for one forward and is never recorded.
661+
662+
**Two sentences above stop being exhaustive when the code half lands**, and that PR corrects
663+
them in the same change: rule 4's "today the OAuth / MCP client door alone", and the 2026-09-16
664+
note's "agent provenance is not representable" on the REST face. A forward credential becomes the
665+
second way a door resolves an agent principal, and it is only ever minted from the first: an agent
666+
the MCP OAuth door already admitted. Until then both sentences stay true as written.
667+
668+
Ruling: director seat, batch #434 item 4, maintainer 「同意」 2026-10-31 — **go, as shaped**
669+
(record 6545950368; the card's shape and triage 6540849042). The cells are named here, before any
670+
code, as the ruling orders.
671+
484672
### D11 — OWD gains an external dimension (`externalSharingModel`)
485673

486674
> **Amended 2026-10-14 (ruling of record 6159300530 on #23268, letter A — the maintainer's 「23268

0 commit comments

Comments
 (0)