Repository navigation
feat(access): the platform role on the Access tab; a user assigned to a companion answers its asks (abilityai/trinity-enterprise#837) - #3634
Merged
Conversation
…to a companion answers its asks (Abilityai/trinity-enterprise#837) PR 2 of Abilityai/trinity-enterprise#837 (AC5 + AC7). - Role route: PUT /api/users/{username}/role refuses every key (reject_non_interactive_principal; require_admin alone admitted trinity-system's key and an admin's user-scoped key, so either could promote anyone to admin), audits role_change (from -> to; no-ops and refusals write none), and points the person's whitelist row at the new role (db.set_whitelist_default_role), so a demotion cannot be undone by re-binding the account away and signing in with the old address. - Access tab (AccessPanel.vue): for an admin whose role the server verified, a role select on every other Active row (every change confirmed; a cancel, Esc or refusal puts the select back; one save at a time), "Give a platform account" on a Pending row (confirmed: a platform account sees the team view), "never signed in". The list's first load goes through viewState and reloads in place (loading-gate baseline 1 -> 0). New domain store stores/users.js on the single API client. - People this agent serves: each person's role as a badge and in the pickers; assigning someone writes the assignment, then shares the agent with them (never the owner, yourself, or an account with no email; a 409 is "already shared"); a warning and "Share with them" on an assignment the agent isn't shared for; who the picker can't offer yet. An assignment alone is not access: the Inbox lists asks only from agents on the person's roster. - Tests: test_837_role_change_audit.py, test_837_assigned_user_inbox.py (a real `user` JWT through the Workspace door and the real roster), accessPanelMembers.mount.spec.js, agentAssignmentsShare.mount.spec.js. Every gate and the assign-then-share order are mutation-proven. - Docs: requirements/auth.md §2.10, the workspace-only-role, role-model, agent-sharing and email-authentication flows, the API catalog and security area, the upgrade note, user docs, the cso diff report, two learnings fragments. The account endpoint behind "Give a platform account" is a separate trinity-enterprise change; the submodule pointer bump follows its merge. No DB migration. Part of Abilityai/trinity-enterprise#837 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…prise#837) From the localhost eyeball of PR 2 (all 11 checks passed): - The role select and "Give a platform account" sit in one fixed-width slot on every row (empty on your own), so Proactive and Remove line up. - No "Platform role · every agent" caption under each select; the header says it and the select's accessible name keeps "(every agent)". - "Share with them" is a link-style (ghost) button, like "Add seat"; the assignment row's Kind select is size sm; the add form's Add button matches the fields' height. - At phone width the share box puts "Add person" under a full-width email field. - Settings > Email Whitelist > Source: a row given from the Access tab reads "Access tab", and any other unlabelled source shows as stored (no source: "—") instead of claiming "Manual" (utils/emailWhitelist.js::whitelistSourceLabel). Tests: the Access spec pins the slot on every row and the caption's absence; the whitelist spec pins the label rule and the mounted Source column. Both mutation-checked. Part of Abilityai/trinity-enterprise#837 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
obasilakis
approved these changes
Oct 12, 2026
obasilakis
left a comment
Contributor
There was a problem hiding this comment.
Validated at e7350a9. Approving the content.
Checked:
- Tier 1 is green, plus
journey-smokeande2e. - The 17 new backend tests pass, and each fix is mutation-proven: removing the interactive-principal gate fails 3 tests, removing the whitelist sync fails 2, inverting the audit condition fails 4.
- The two component specs mount the components (no source-text reads).
AccessPanel.vueraw-color count is unchanged from dev (3), and the loading-gate baseline drops 1 → 0.- No secrets, host paths, or enterprise-design disclosure in the diff.
Two notes for the author:
- The whitelist sync runs on promotion as well as demotion. Raising someone to
adminsets their address's row toadmin, so if that account later re-binds to another address, the old address signs in as a new admin account. The CSO report covers the demotion direction only. Please confirm this is intended. - The role, the audit row and the whitelist row are written in sequence. A failure in the whitelist write returns 500 with the role already changed. Low severity.
Merge order per the PR body still applies: trinity-enterprise#874 merges first, then the submodule-bump commit lands here and Tier 1 re-runs on it. status-in-dev on trinity-enterprise#837 needs setting by hand after merge, since the closing reference is cross-tracker.
…tes it first (Abilityai/trinity-enterprise#837) Review on #3634 (two notes): 1. The whitelist sync also RAISED the row on a promotion. Raised to admin, a person who re-binds their account to another address could sign in with the old one as a second admin account — one a later demotion of the first would not reach. The row is now capped at the new role: lowered, never raised (db.cap_whitelist_default_role, one conditional UPDATE, replacing the setter). 2. The role, the audit row and the whitelist row were written in that order, so a failed whitelist write answered 500 with the role already changed. The cap now runs first; a failure leaves the role unchanged, and the only partial state is a row already lowered. The whitelist module's role copy is now an ordered tuple pinned to ROLE_HIERARCHY by a test. New tests: a promotion never raises the row (re-bind replay), a row below the new role stays, a failed whitelist write leaves the role unchanged, the order pin. Mutation-proven: letting the row rise fails 2; capping after the write fails 1. Part of Abilityai/trinity-enterprise#837 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1d3c) Carries trinity-enterprise#874 (squash f8d1d3c on private main): the account endpoint behind the Access tab's "Give a platform account" (Abilityai/trinity-enterprise#837). f8d1d3c has the same tree as the reviewed head 90c6b5f. Enterprise migration graph: one head (scripts/ci/check_alembic_heads.py). Part of Abilityai/trinity-enterprise#837 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Thanks for the review. Both notes are addressed in
Pointer bump:
|
This was referenced Oct 12, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR 2 of abilityai/trinity-enterprise#837 (AC5 + AC7): the platform role on Agent Detail › Access, and a
userassigned to a companion answers its asks in the Workspace Inbox.PUT /api/users/{username}/rolerefuses every key (reject_non_interactive_principal—require_adminalone admittedtrinity-system's key and an admin's user-scoped key, so either could promote anyone to admin), writes arole_changeaudit row (from → to; no-ops and refusals write none), and points the person's whitelist row at the new role, so a demotion can't be undone by re-binding the account away and signing in with the old address.viewState; loading-gate baseline 1 → 0). Everyone else keeps the read-only label and the pointer to Settings.Name · Workspace only). Assigning someone writes the assignment, then shares the agent with them (never the owner, yourself, or an account with no email; 409 = already shared) — an assignment alone isn't access: the Inbox lists asks only from agents on the person's roster. An admin sees "Not shared with …" + Share with them on an assignment the agent isn't shared for.Changes
routers/users.py(gate, audit, whitelist sync),db/email_auth.py+database.py(set_whitelist_default_role)AccessPanel.vue,AgentAssignmentsSection.vue,AgentDetail.vue,Settings.vue, newstores/users.js,utils/workspaceOnly.js,utils/emailWhitelist.js,loading-gate-baseline.jsontests/unit/test_837_role_change_audit.py,tests/unit/test_837_assigned_user_inbox.py,accessPanelMembers.mount.spec.js,agentAssignmentsShare.mount.spec.js,emailWhitelist.spec.js;tests/registry.jsonrequirements/auth.md§2.10, the workspace-only-role / role-model / agent-sharing / email-authentication flows, the API catalog + security area, the upgrade note, user docs,docs/security-reports/cso-diff-2026-10-12-ent837-access-role.*, two learnings fragmentsTest Plan
test_837_role_change_audit.py(15),test_837_assigned_user_inbox.py(2 — a realuserJWT through the Workspace door and the real roster). Every gate, the audit, the whitelist sync and the assign-then-share order are mutation-proven.markdownCodeBlocks,roomComposerChain,roomStopWorkfail as on dev)./verify-local --skip-agent: build + import smoke, boot + health, test preflight OK; integration 69 passed, 13 skipped (agent-dependent with--skip-agent, no PostgreSQL), 2 deselected (known false fails). Unit stage: 25 479 passed; the 3test_3262system-seed failures appear only underTRINITY_DEFAULT_SYSTEM_MANIFEST=disabled(10/10 without)./cso --diff: 2 pre-existing findings fixed here (machine keys on the role route; a stale whitelist role undoing a demotion); report indocs/security-reports/.Merge order
The account endpoint behind Give a platform account is a private-module change, abilityai/trinity-enterprise#874. That PR merges first; this PR then gets one more commit bumping the enterprise submodule pointer to its merge commit (
b5080b6 -> <merge>). Don't merge this PR before that commit lands. No DB migration.Fixes abilityai/trinity-enterprise#837
🤖 Generated with Claude Code