Repository navigation
feat(platform-objects): the identity actions an agent may reach declare it — additive acts exposed, destructive lifecycle acts behind confirmation, credential and ownership acts human-only - #28270
Conversation
…re it, by risk Bucket E actions declare ai.exposed with a model-facing description; bucket C actions also declare requiresConfirmation: true; human-only actions carry no ai block. ADR-0011 gains a dated addendum recording the three buckets and the H rule. The MCP dogfood drives one action per bucket with an OAuth-connected agent, and two tests whose premise was "no platform identity action declares ai" follow the declarations. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4czk9umSFBVoz5cijzX
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract-review-claim: The lane seat runs the |
…C override set aside leave_organization now declares ai.requiresConfirmation: true (ADR-0011's 2026-10-30 addendum, bucket C), and the declared override is read before the variant, so "still destructive off variant alone" passed without reading the variant. The case now asserts the declared flag on its own and classifies the real declaration with the override set aside; dropping the variant there turns it red. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4czk9umSFBVoz5cijzX
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract-review-claim: Delta over |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
1 similar comment
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Seat record · PR #28270 · head
|
Requested by Elon · project thread
domain:server#3 · os-elon-musk · session_016WV4czk9umSFBVoz5cijzX
Fixes #27900
Ruling-ref: 6523087116 (letter A+)
Tier: H (
docs/adr/0011-actions-as-ai-tools.md): a contract review, then an authorised approval by a GOVERNED_APPROVER, before the queue.Clause-②: no (values on an existing metadata key, platform-owned; no export, answer member, status name or error code is added, and the MCP doors' code is unchanged: their existing per-action opt-in now admits the actions that declare it)
Metadata-protocol: none
Skills-docs: updated here —
content/docs/ai/connect-mcp.mdxgains one bullet (which identity actions an MCP agent sees, and what an OAuth-connected agent gets today);grep -rniE "sys_(user|member|invitation|team|organization|session|api_key|oauth|sso|two_factor|account)\b.{0,80}(ai|agent|mcp|exposed)|(ai\.exposed|exposed).{0,120}sys_" skills content/docsfound no skill or doc naming which platform identity actions are agent-exposed.维护者速读
ai: { exposed: true, description }(给模型看的说明);C 档 17 个另加requiresConfirmation: true,MCP 不带confirm: true就 428 拒绝、什么都不执行;H 档 20 个不写ai块,不在列表里,按名拒绝。ADR-0011 新增 2026-10-30 附记,列全三档名单和 H 档规则。没有新增任何自动检查。ai块而不是 39 个。名字为准。sys_invitation.copy_invitation_link是评估之后才加的(复制邀请链接,链接本身就是凭据),保持不声明,等您点名裁。sys_user.enable_two_factor/sys_two_factor.enable_two_factor要用户当前密码作参数,返回认证器 URI 和一次性备用码。暴露给 agent 就意味着 agent 向用户要密码、并拿到新的登录因子,这接近 H 档的「铸造凭据」。按卡原样落了;您一句话可以把它们移到 H(删两个块 + ADR 一行)。What changes
Declarations (
packages/platform-objects/src/identity/*.object.ts, 12 files). Each action the ruling places in bucket E gainsai: { exposed: true, description }, with a description written for the model (what it does, its params, when to call it). Each bucket C action gains the same plusrequiresConfirmation: true, and its description ends by asking the model to confirm with the human first. Bucket H actions are untouched and carry noaiblock. Each block sits last in its action, under a one-line comment naming the bucket.invite_usermirrors (sys_user,sys_invitation,sys_organization,sys_member),sys_invitation.resend_invitation,sys_invitation.accept_invitation,sys_member.add_member,sys_team_member.add_team_member,sys_team.add_team_member,sys_team.create_team,sys_team.update_team,sys_organization.create_organization,sys_organization.update_organization,sys_organization.rename_organization,sys_organization.set_active_organization,sys_oauth_application.enable_oauth_application,sys_user.enable_two_factor,sys_two_factor.enable_two_factor,sys_user.update_my_profile,sys_user.resend_verification_email,sys_sso_provider.request_domain_verificationsys_user.ban_user,sys_user.unban_user,sys_user.unlock_user,sys_user.set_user_manager,sys_api_key.revoke_api_key,sys_session.revoke_session,sys_session.revoke_my_other_sessions,sys_invitation.cancel_invitation,sys_invitation.reject_invitation,sys_member.remove_member,sys_team_member.remove_team_member,sys_team.remove_team,sys_oauth_application.disable_oauth_application,sys_oauth_application.delete_oauth_application,sys_sso_provider.delete_sso_provider,sys_organization.leave_organization,sys_account.unlink_accountaiblock (listed in the ADR addendum)ADR-0011 addendum (2026-10-30). Records the ruling, the rubric, all three name lists, the H rule (no
ai.exposedon an H action without a maintainer ruling naming it), the count correction,copy_invitation_linkas not assessed, the struck automatic check, how the declarations reach an agent (pointing at ADR-0066 D4's 2026-10-30 note for the mechanism and naming these 38 as the platformsys_*actions that declare it), and what an OAuth-connected agent gets today. This keeps the two ADR notes consistent, per the skills note on the card (6523989016): ADR-0066's note states the per-action mechanism; this addendum says which actions use it.Docs. One bullet in
content/docs/ai/connect-mcp.mdxunder the exposure policy.Changeset.
@objectstack/platform-objectspatch.Why
Ruling 6523087116 (letter A+) on #27900 executes parameter 2 of ruling 6513869839 on #27681 as one assessment of the platform's kernel actions, as the maintainer asked (「但是对于系统内核对象,是否应该统一评估一下。」), and settles that the actions allowed to open get their declarations now: 「所以我说评估标准对象的意思,就是允许放开的动作,是不是就应该把申明加上」. The maintainer struck the automatic check (「自动检查 有必要吗?…」), so the table lives only in the ADR.
Premise re-read (dispatch step 1)
Read on
main@be646ede, then rebased onto744ad0ad. All 58 names the card lists exist on the object and under the name the card gives. Two differences from the card, reported to the seat before any code, and the seat ruled the way on:sys_invitation.copy_invitation_link(typescript) exists onmainand in no bucket; it is absent at the card's read shaded67bfb. It keeps noaiblock, today's state; the addendum names it as not assessed.The MCP dogfood, one case per bucket
packages/qa/dogfood/test/mcp-system-object-action-exposure.dogfood.test.ts(rewritten; booted showcase, live MCP transport, better-auth admin plugin on, embedded OAuth authorization server on). The test-onlyaiblock it used to set onban_useris gone: it reads the platform's own declarations, and a premise check asserts one action per bucket (ban_userC,update_my_profileE,impersonate_userH). It is not a pin over the table.The OAuth-connected agent is the caller the ruling exposes these actions to. Its token is minted the way an MCP client connects: dynamic client registration, authorize with
resource= the MCP URL and PKCE S256, consent, token (scopesdata:read data:write actions:execute).update_my_profilelisted,aiExposed: true,requiresConfirmation: false401 UNAUTHORIZEDfrom the identity route (/api/v1/auth/update-user); the name is unchangedok: true, the name changes428 ACTION_CONFIRMATION_REQUIRED, nothing run401 UNAUTHENTICATED "Sign in first"from/api/v1/auth/admin/ban-user; nobody bannedimpersonate_userabsent from the listing, refused by nameThe #27681 pins stay on the administrator's session, now on the platform declaration:
ban_userlisted and attested,428unconfirmed,409 ACTION_DISABLEDwith the ADR-0126 switch off, a plain member's confirmed call refused by the target, the confirmed admin call bans. The control moved fromunban_user(now C, exposed) toimpersonate_user(H).What waits on #28178. The ruled outcome of the E run (an OAuth agent's call served) cannot be shown without changing which credentials an identity route or the REST face accepts, which #28178 governs. Per the dispatch, this PR does not build around it: the run case pins today's
401and says it waits on #28178, and the session contrast shows the declaration itself is live. When #28178 lands, flip the two "today" expectations. For the record, the agent's listing onsys_useris exactly the eight E and C actions there (invite_user,ban_user,unban_user,unlock_user,set_user_manager,update_my_profile,resend_verification_email,enable_two_factor).Two tests whose premise the declarations changed (test-only edits, surface amended by the seat)
packages/platform-objects/src/identity/action-confirm-one-dialog.test.ts: it asserted that the platform-objects: 16 more actions declare bothconfirmTextandparams, so one click opens two sequential dialogs (same shape as #7278) #7309-converted actions have noai.description, so that the dialog question was never moved into the tool description. Seven of them now carry a model-facingai.descriptionby the ruling. The assertion now checks the intent directly: each still has its dialogdescription, and itsai.descriptionis not that question. Nothing else in the file changed.packages/runtime/src/action-execution-destructive.test.ts: it asserted that all 13 platform-objects: 16 more actions declare bothconfirmTextandparams, so one click opens two sequential dialogs (same shape as #7278) #7309 identity actions are excluded at the MCP gates because none declaresai.exposed. Seven of the 13 are now exposed (C:ban_user,disable_oauth_application,delete_oauth_application,unlink_account,delete_sso_provider,remove_team_member; E:enable_oauth_application). The six human-only ones keep both exclusion pins unchanged. The seven are pinned admitted at gates 1 and 3, with the listing'srequiresConfirmationequal to the declared flag.disable_oauth_applicationleft the "noaioverride, not destructive" list because it now declaresrequiresConfirmation: true, and a new case pins that the declared override wins (the precedence the file already pins). No assertion was dropped for an action whose declaration did not change.Verification
0bd14911: 11/11 (five of the file's six prior cases, plus the session contrast and the four OAuth cases; the oldunban_usercontrol case became theimpersonate_userone). Run beside it,api-action-door-forward.dogfood.test.tsandtwo-factor-backup-code-reveal.dogfood.test.tsstay green: 3 files, 25/25.@objectstack/platform-objectsdist, then restored and rebuilt): withban_user'srequiresConfirmationremoved and anaiblock added toimpersonate_user(premise checks taken out so the cases speak), 8 of 11 red: the session listing, the sessionimpersonate_userrefusal, both428cases, the agent's H case, and three cascades (the switch, the confirmed ban and the session contrast, after the unguarded calls had run). Restored: 11/11.@objectstack/platform-objectssuite: 104 files, 1474/1474.packages/runtime/src/action-execution-destructive.test.ts: 57/57. Runtime suite: 490 files, 8854 passed, 19 skipped.@objectstack/mcpsuite: 47 files, 603/603.pnpm --filter @objectstack/platform-objects typecheckandpnpm --filter @objectstack/runtime typecheckexit 0; the dogfood package'stsc --noEmit(8 GB heap) exits 0.dispatch-gates --commandsderives (94) ran after a whole-tree build (pnpm build, 115/115) and exited 0, reconciled with--ran: 94 run, 0 NOT-MEASURED. One of them,check-plugin-teardown-shape.mjs --self-test, first exited 3 because the shallow clone could not read its pinned fixture commit; after fetching that commit it exited 0 (48 cases).pnpm lintexits 0.Acceptance notes
enable_two_factorrows (E) take the user's current password as a param and answer the authenticator URI and one-time backup codes. Exposed to an agent, the agent asks for the password and receives fresh sign-in factors, close to the rubric's H test. Landed as the card lists them; the seat has taken the question to the maintainer.defaultFromRowparams from the record, so a C action likedelete_sso_providernames itsproviderIdparam in the description.Human Minimum
packages/specholds no runtime logic. (Not touched.)pnpm lintpasses.main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed. (Not touched.)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4czk9umSFBVoz5cijzX