You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 9b4d193
Browse filesBrowse the repository at this point in the historyBrowse files
fix(platform-objects): enabling two-factor and copying an invitation link are human-only identity actions (#28386)
<!-- claude-projects-attribution: {"github_login":"os-zhuang"} -->
_Requested by **Sam** · [project
thread](https://claude.ai/code/project/chan_01LjZwNRFiG2fkp8SNf6rHNx?thread=cmsg_01LjZwNRFiG2fkp8SNf6rHNxA5aNWp4A2SV4FZHR1jhLti)_Fixes#28368
Clause-②: no
Metadata-protocol: none
domain:server#4 · os-zhuang · os-dev for claim 6541181717 · Ruling-ref:
6540309323 (#27900, 「其他同意」)
Tier: H (`docs/adr/0011-actions-as-ai-tools.md`): a contract review,
then an authorised approval by a GOVERNED_APPROVER, before the queue.
## 维护者速读
- 改了什么:两个 `enable_two_factor` 动作(`sys_user` 与 `sys_two_factor`)删掉 `ai`
块,从 E 档移到 H 档;`sys_invitation.copy_invitation_link` 评为 H 档,保持不写 `ai`
块。ADR-0011 附记更新三档名单(H23 / C17 / E19,共 36 个 `ai` 块、59
个动作),并加一行:`sys_approval_request` 的 9 个动作未评估,评估前一律仅限人工。
- 为什么改:执行您在 #27900 上的裁决「其他同意」。启用双因素要用户密码、返回新的登录因子;邀请链接本身就是凭据。两者都不该让
agent 拿到。
- 风险与回滚:只收窄 agent 能看到的动作,Console 里登录用户的操作不变。回滚即还原这一个提交。
- 先看:`docs/adr/0011-actions-as-ai-tools.md`
的修订注记与名单;`packages/platform-objects/src/identity/sys-user.object.ts` 的
`enable_two_factor`。
- 两分钟验证:`grep -c "ai: {"
packages/platform-objects/src/identity/*.object.ts` 合计应为 36,且
`enable_two_factor` 下不再有 `ai:`。
- 需要你做的:本 PR 触及 ADR(Tier H),合同复审通过后需 os-zhuang 或 hotlong 批准。
## What changes
- **`sys_user.enable_two_factor`, `sys_two_factor.enable_two_factor` (E
→ H).** Each drops its `ai: { exposed: true, description }` block; a
one-line comment names bucket H in its place. Over MCP neither is listed
any more, and `run_action` refuses both by name (the existing per-action
opt-in, `systemObjectActionExposureError`). A signed-in person runs both
exactly as before.
- **`sys_invitation.copy_invitation_link` (assessed H).** No `ai` block,
as before; a one-line comment names bucket H and why (the accept link is
a bearer credential). No behaviour change.
- **ADR-0011, the 2026-10-30 identity addendum.** A dated amendment note
(ruling of record 6540309323), the H list gains the three names (23),
the E list loses two (19), the count paragraph reads 59 actions / 36
`ai` blocks, the "Not assessed" paragraph now records
`sys_approval_request`'s 9 actions as unassessed and human-only, and the
two "38 / 37" counts in the MCP paragraphs read 36 / 35.
- **Changesets.** New
`28368-identity-two-factor-invite-link-human-only.md`
(`@objectstack/platform-objects` patch). The unreleased
`27900-identity-actions-ai-exposure.md` is corrected to the final lists
(19 / 17 / 23, 59 assessed, 36 declared) so the release does not ship a
sentence saying two-factor enabling is exposed.
### Reading of the card's item 1
The card says `copy_invitation_link` "gets an `ai` block, tier H". The
ruling of record (6540309323) says "→ H (human-only)", the ADR's H row
says "no `ai` block", and the ruled total, 36 `ai` blocks = C17 + E19,
leaves no room for a 37th. So H here means no `ai` block; the action
keeps today's declaration, and the change is its entry in the H list
plus the code comment.
## Tests
- `pnpm --filter @objectstack/platform-objects test`: 104 files, 1474 /
1474 passed.
- `pnpm --filter @objectstack/platform-objects typecheck`: exit 0.
- Build of the dependency closure (`pnpm --filter
'@objectstack/platform-objects...' build`): exit 0.
- `git grep` of `enable_two_factor` and `copy_invitation_link` across
`packages/runtime`, `packages/qa`, `packages/services` and
`content/docs` found no test or doc pinning either action as
agent-exposed. #28270 added no per-tier name-list pin (the maintainer
struck the automatic check), so no pin moves.
## Acceptance notes
- **`Metadata-protocol: none`.** The claim first read `narrowed`; the
seat amended it to `none`, since the kind is defined by what a metadata
author can write and these are values on an existing key of
platform-owned metadata (the same kind #28270 declared). So the
changeset is `patch` and carries no ADR-0087 marker.
- **`check-empty-changeset` is red on purpose.** This PR corrects the
still-unreleased `.changeset/27900-identity-actions-ai-exposure.md`
(#28270's note) to the final lists: exposed 19 (enabling two-factor
removed), human-only 23 (enabling two-factor and copying an invitation
link added), 59 assessed / 36 declared. Without it, the release would
ship a sentence saying enabling two-factor is agent-exposed. The gate
puts this DELIBERATE CORRECTION in front of a person by staying red;
please confirm it at landing.
- The ruling also asked for one note line on `sys_approval_request`; its
9 actions (`plugin-approvals`) were measured to declare no `ai` block,
so the note matches the code.
## Verification
Head `cbd23194`, merge base `fc5d5093` (`origin/main`).
<details><summary>Local gates: 61 derived, 61 run, 60 exit 0, 1 red by
design</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 61 commands from 6 paths (69 changed
lines).
- All 61 run on head `cbd23194` after a full `pnpm build` (116/116, exit
0). 60 exit 0.
- `node scripts/check-empty-changeset.mjs --base origin/main`: exit 1,
the DELIBERATE CORRECTION of
`.changeset/27900-identity-actions-ai-exposure.md` described under
Acceptance notes. It stays red until a person confirms.
- `dispatch-gates --ran` with exit codes recorded: 61 derived, 61 run, 0
NOT-MEASURED (a derived zero), exit 0.
- `pnpm lint`: exit 0.
- The first pass, before the full build, gave exit 3 (PREREQUISITE NOT
MET) on `check:dual-build-cjs-loads`, `check:i18n`,
`check:lean-entry-closure` and `check-plugin-teardown-shape.mjs
--self-test` (shallow clone). All four exit 0 on the final pass.
</details>
<details><summary>Not measured locally</summary>
- The CI-only families `dispatch-gates` names outside its derived total
(artifact rosters, wide-population families, the Build Core and Type
Check lanes) are left to this PR's check-runs.
- The dogfood battery is not owed: there is no `!` in the title and no
`(narrowing)` arm.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QUs3BrhuPB4AGHyXk3rvfh)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: .changeset/27900-identity-actions-ai-exposure.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,10 +7,10 @@ feat(platform-objects): the platform identity actions an agent may reach declare
7
7
Clause-②: no
8
8
Metadata-protocol: none
9
9
10
-
The `sys_*` identity objects' actions now carry the ADR-0011 `ai` block by risk, as ADR-0011's 2026-10-30 addendum records (58 actions assessed, 38 declared):
10
+
The `sys_*` identity objects' actions now carry the ADR-0011 `ai` block by risk, as ADR-0011's 2026-10-30 addendum records (59 actions assessed, 36 declared):
11
11
12
-
-**Exposed (21).** Additive or self-scoped acts declare `ai: { exposed: true, description }`: the four `invite_user` mirrors, resending and accepting an invitation, adding a member to an organization or a team, creating, editing and renaming a team or an organization, switching the active organization, re-enabling an OAuth application, enabling two-factor authentication, updating your own profile, resending the verification email, and requesting SSO domain verification.
12
+
-**Exposed (19).** Additive or self-scoped acts declare `ai: { exposed: true, description }`: the four `invite_user` mirrors, resending and accepting an invitation, adding a member to an organization or a team, creating, editing and renaming a team or an organization, switching the active organization, re-enabling an OAuth application, updating your own profile, resending the verification email, and requesting SSO domain verification.
13
13
-**Exposed, confirmation required (17).** Destructive or privilege-changing lifecycle acts also declare `requiresConfirmation: true`, so MCP `run_action` refuses them `428 ACTION_CONFIRMATION_REQUIRED`, with nothing run, until the call carries `confirm: true`: banning, unbanning and unlocking a user, setting a user's manager, revoking an API key or a session, signing out other devices, cancelling or declining an invitation, removing a member or a team member, deleting a team, an OAuth application or an SSO provider, disabling an OAuth application, leaving an organization, and unlinking a sign-in provider.
14
-
-**Human-only (20).** Acts that mint a credential or a session, change ownership, a role or a sign-in factor, or destroy an account or an organization (impersonating a user, setting a password, creating a user or an API key, transferring ownership, changing a member's role, deleting an organization, and the rest the addendum names) declare no `ai` block and are not listed over MCP. `sys_invitation.copy_invitation_link` declares none either; it was not part of the assessment.
14
+
-**Human-only (23).** Acts that mint a credential or a session, change ownership, a role or a sign-in factor, or destroy an account or an organization (impersonating a user, setting a password, enabling two-factor authentication, creating a user or an API key, copying an invitation's accept link, transferring ownership, changing a member's role, deleting an organization, and the rest the addendum names) declare no `ai` block and are not listed over MCP.
15
15
16
16
Over MCP these actions are listed and run behind every existing gate: the capability gate, the installation's action switch, the param contract, the confirmation gate and the identity route's own authorization. They are `type: 'api'` actions forwarded to identity routes that accept a signed-in session only, so an agent connected over OAuth sees them listed and is refused at the route when it runs one.
fix(platform-objects): enabling two-factor authentication and copying an invitation's accept link are human-only — an agent cannot reach them over MCP
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
`sys_user.enable_two_factor` and `sys_two_factor.enable_two_factor` no longer declare an ADR-0011 `ai` block. Each takes the user's current password and answers a new sign-in factor (the authenticator URI and one-time backup codes), so an MCP agent no longer sees them in `list_actions` and `run_action` refuses them by name. `sys_invitation.copy_invitation_link`, which answers the invitation's accept link (a bearer credential), is assessed human-only and keeps declaring no `ai` block. A person signed in to the Console runs all three exactly as before.
11
+
12
+
ADR-0011's identity-actions addendum records the amended lists (human-only 23, exposed with confirmation 17, exposed 19: 36 `ai` blocks over 59 actions) and notes that `sys_approval_request`'s 9 actions are unassessed and human-only until assessed.
Copy file name to clipboardExpand all lines: docs/adr/0011-actions-as-ai-tools.md
+19-13Lines changed: 19 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -324,15 +324,21 @@ three buckets, and the declarations were written in the same change as this adde
324
324
|**C** exposed, confirmed | a destructive or privilege-changing lifecycle act that is reversible or bounded |`ai: { exposed: true, description, requiresConfirmation: true }`. ⛔ The confirmation is never relaxed. |
325
325
|**E** exposed | an additive or self-scoped act with no credential and no privilege escalation |`ai: { exposed: true, description }`; confirmation is the author's choice (none is declared today). |
// ADR-0011 identity-actions note, bucket E: exposed by plain declaration.
97
-
ai: {
98
-
exposed: true,
99
-
description:
100
-
'Turn on two-factor authentication for the caller\'s own account (param: the current password). Answers the authenticator URI and one-time backup codes; the user must save the codes, which are shown only once.',
101
-
},
96
+
// ADR-0011 identity-actions note, bucket H: human-only, no `ai` block.
97
+
// It takes the user's password and answers a new sign-in factor.
// ADR-0011 identity-actions note, bucket E: exposed by plain declaration.
737
-
ai: {
738
-
exposed: true,
739
-
description:
740
-
'Turn on two-factor authentication for the caller\'s own account (param: the current password). Answers the authenticator URI and one-time backup codes; the user must save the codes, which are shown only once.',
741
-
},
736
+
// ADR-0011 identity-actions note, bucket H: human-only, no `ai` block.
737
+
// It takes the user's password and answers a new sign-in factor.
0 commit comments