Public: anyone can read this; the crew can change it.

An org admin adds a person to a team with `a` and removes the member under the cursor with `d` after a confirm, from the terminal's Teams screen #8133

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The terminal's Organization teams screen now lets an org admin change who is on a team. On a team's member list, `a` asks for a username and adds that person. `d` asks `remove @name from <team>? y/n`: `y` removes the member under the cursor, and any other key leaves the team as it was. The member list redraws after the change, and so does the count on the teams list behind it. Lines are re-read on…

shipped production is serving it deploy e8d1618

opened this

The terminal's Organization teams screen now lets an org admin change who is on a team. On a team's member list, a asks for a username and adds that person. d asks remove @name from <team>? y/n: y removes the member under the cursor, and any other key leaves the team as it was. The member list redraws after the change, and so does the count on the teams list behind it. Lines are re-read only after a write, never on every frame. The screen's header comment no longer says it is read-only.

Admin gate: the screen checks orgAccess(...) === "admin" once when the member list opens, the same rule the page's requireOrgAdmin uses. Anyone else gets no a/d in the help line, and pressing them does nothing.

One change from the issue: the issue names addTeamMember/removeTeamMember in src/core/orgs.ts. The screen instead calls the functions with the same names in src/actions/orgs.ts. Those are what POST /:owner/-/teams/:team/add and /remove actually call: they check admin rights again, look the person up by username, refuse to remove the last owner, and record team.member_* events. Calling the core functions would have skipped all of that.

Messages: they use the page's wording. A refused add ("No account called …", "… is an agent: …") keeps the prompt open with the reason in it. A successful add, "already on", a removal and a refused removal (last owner) appear as a toast. A leading @ on the typed username is dropped.

Tests: test/tui-org-teams.test.ts has three new tests:

  • an add, including the refusal for a name with no account
  • a declined remove followed by a confirmed one, with the count redrawn
  • a non-admin who is shown neither key and cannot change the team

With the screen change taken out, the add and remove tests fail. The non-admin test passes either way, since the keys didn't exist before; it would fail if the admin check were removed.

Checks: bun scripts/gate.ts and bun x tsc --noEmit are clean. scripts/fast.ts named 220 test files (about 18 minutes, over its budget), so I ran the eight closest to the change instead (org-teams, screens, palette entries, teams, prompts, passkeys-remove, adversarial surfaces, xss-everywhere): 75 pass. The forge's full check covers the rest.

Not done: creating or deleting teams, or changing a team's permission, from the terminal; the browser still does those.

To try it: sign in to the terminal as an org owner, open the palette with ctrl+k, choose "Organization teams", press enter on a team, then a to add someone or d to remove the selected member.

🤖 Generated with Claude Code

Closes #8125

commented

The terminal's Organization teams screen now lets an org admin change who is on a team. On a team's member list, a asks for a username and adds that person. d asks remove @name from <team>? y/n: y removes the member under the cursor, and any other key leaves the team as it was. The member list redraws after the change, and so does the count on the teams list behind it. Lines are re-read only after a write, never on every frame. The screen's header comment no longer says it is read-only.

Admin gate: the screen checks orgAccess(...) === "admin" once when the member list opens, the same rule the page's requireOrgAdmin uses. Anyone else gets no a/d in the help line, and pressing them does nothing.

One change from the issue: the issue names addTeamMember/removeTeamMember in src/core/orgs.ts. The screen instead calls the functions with the same names in src/actions/orgs.ts. Those are what POST /:owner/-/teams/:team/add and /remove actually call: they check admin rights again, look the person up by username, refuse to remove the last owner, and record team.member_* events. Calling the core functions would have skipped all of that.

Messages: they use the page's wording. A refused add ("No account called …", "… is an agent: …") keeps the prompt open with the reason in it. A successful add, "already on", a removal and a refused removal (last owner) appear as a toast. A leading @ on the typed username is dropped.

Tests: test/tui-org-teams.test.ts has three new tests:

  • an add, including the refusal for a name with no account
  • a declined remove followed by a confirmed one, with the count redrawn
  • a non-admin who is shown neither key and cannot change the team

With the screen change taken out, the add and remove tests fail. The non-admin test passes either way, since the keys didn't exist before; it would fail if the admin check were removed.

Checks: bun scripts/gate.ts and bun x tsc --noEmit are clean. scripts/fast.ts named 220 test files (about 18 minutes, over its budget), so I ran the eight closest to the change instead (org-teams, screens, palette entries, teams, prompts, passkeys-remove, adversarial surfaces, xss-everywhere): 75 pass. The forge's full check covers the rest.

Not done: creating or deleting teams, or changing a team's permission, from the terminal; the browser still does those.

To try it: sign in to the terminal as an org owner, open the palette with ctrl+k, choose "Organization teams", press enter on a team, then a to add someone or d to remove the selected member.

🤖 Generated with Claude Code

Changes requested — before the latest push

The logic is sound. It calls the actions-layer addTeamMember/removeTeamMember, so admin is re-checked and events are emitted. The y/n confirm and the tests are good. One change is needed: README.md:78 still says the Organization teams screen "is read-only". Say that an org admin adds a person with a and removes the member under the cursor with d after a y/n, and that a member sees neither key. The README has to say how to use the feature.

commented

An org admin can now change who is on a team from the terminal's Organization teams screen. On a team's member list, a asks for a username and adds that person. d asks remove @name from <team>? y/n; y removes the member under the cursor and any other key leaves the team as it was. Someone who is not an org admin does not see either key in the help line, and pressing them does nothing. The member list and the team's count redraw after a change. They are re-read only after a change, not on every frame.

What to look at:

  • Actions: the screen calls addTeamMember and removeTeamMember from src/actions/orgs.ts, not the ones the issue names in src/core/orgs.ts. The actions layer is what POST /:owner/-/teams/:team/add and /remove call. It checks the admin again, looks the person up by name, refuses to remove the last owner, and records the add or remove as an event.
  • Shared wording: the messages for these outcomes now live in one new module, src/actions/team_words.ts, used by both the Teams page (src/web/pages/org.ts) and the screen, so the two cannot drift apart. The page shows the same text as before. In the terminal, a refused add keeps the username prompt open with the reason; every other outcome shows as a toast.
  • README: the line that called the screen read-only now describes a and d, and says a member who is not an org admin is offered neither.

Tests: test/tui-org-teams.test.ts covers an add (including the "No account called …" refusal), a declined then a confirmed remove with the count redrawn, and a non-admin who is offered neither key. The add and remove tests fail without the screen change. The non-admin test passes either way, since the keys did not exist before; it would fail if the admin check were dropped. The gate, the type check and the design lint are clean. scripts/fast.ts named 220 test files (about 18 minutes), so I ran the closest ones instead: this suite, confirm-team-remove, the README tests, and every test file with "org" in its name. All pass; 2 were skipped.

Not done: creating or deleting teams, or changing a team's permission, from the terminal.

To try it: sign in to the terminal as an org owner, press ctrl+k, choose Organization teams, press Enter on a team, then a to add someone or d to remove the selected member.

🤖 Generated with Claude Code

commented

On the terminal's Organization teams screen (src/tui/screens/orgteams.ts), an org admin can now change who is on a team. On a team's member list, a asks for a username and adds that person through addTeamMember. d asks remove @name from <team>? y/n and removes the member under the cursor through removeTeamMember only on y. Refusals use the Teams page's words, which now come from src/actions/team_words.ts, shared with src/web/pages/org.ts. After a change the member list and the team's count are read again. Anyone who is not an org admin (the same orgAccess gate as requireOrgAdmin) is not shown either key, and pressing them does nothing. test/tui-org-teams.test.ts covers an add, a confirmed remove, a declined remove, and a non-admin who can do neither.

Look at the merge: while this pull was open, main reverted #8102, the read-only screen this pull builds on (#8139). The revert's bisect blamed #8102 for MCP backups_test > an unreachable one says why, and never echoes a secret and another test in test/mcp-backups.test.ts. Sentinel's #8128 reports that first test failing on 3 other pulls, so the screen is probably not the cause. The merge keeps the screen and its test, adds orgTeams back to src/tui/screens/entries.ts, and keeps the README paragraph. Rerunning #8102 adds this back to main; if you want it to stay reverted, close this pull instead.

Checks after the merge: scripts/gate.ts and tsc --noEmit pass. test/tui-org-teams.test.ts, test/mcp-backups-test.test.ts, test/mcp-backups.test.ts and the palette tests pass (56 pass, 5 skip, 0 fail). test/spawn-cell-e2e.test.ts, the third file the revert named, only skips here because the sandbox cannot run cells, so the forge's check is the first real run of it.

Not done: there is no MCP tool for adding or removing team members.

To try it: open the palette with :, choose Organization teams, press Enter on a team, then a or d.

🤖 Generated with Claude Code

commented

On the terminal's Organization teams screen (src/tui/screens/orgteams.ts), an org admin can now change who is on a team. On a team's member list, a asks for a username and adds that person through addTeamMember. d asks remove @name from <team>? y/n and removes the member under the cursor through removeTeamMember only on y. Refusals use the Teams page's words, which now come from src/actions/team_words.ts, shared with src/web/pages/org.ts. After a change the member list and the team's count are read again. Anyone who is not an org admin (the same orgAccess gate as requireOrgAdmin) is not shown either key, and pressing them does nothing. test/tui-org-teams.test.ts covers an add, a confirmed remove, a declined remove, and a non-admin who can do neither.

Look at the merge: while this pull was open, main reverted #8102, the read-only screen this pull builds on (#8139). The revert's bisect blamed #8102 for MCP backups_test > an unreachable one says why, and never echoes a secret and another test in test/mcp-backups.test.ts. Sentinel's #8128 reports that first test failing on 3 other pulls, so the screen is probably not the cause. An earlier merge on this branch keeps the screen and its test, adds orgTeams back to src/tui/screens/entries.ts, and keeps the README paragraph. Rerunning #8102 adds this back to main; if you want it to stay reverted, close this pull instead. The latest merge of main (#8120: changes to src/tui/app/app.ts, test helpers and test/egress-admin.test.ts) had no conflicts.

Checks after the merges: scripts/gate.ts and tsc --noEmit pass. After the latest merge, test/tui-org-teams.test.ts, test/tui-app.test.ts and test/egress-admin.test.ts pass (19 tests, 0 fail). After the earlier merge, both MCP backups test files and the palette tests passed (56 pass, 5 skip, 0 fail). test/spawn-cell-e2e.test.ts, the third file the revert named, only skips here because the sandbox cannot run cells, so the forge's check is the first real run of it.

Not done: there is no MCP tool for adding or removing team members.

To try it: open the palette with :, choose Organization teams, press Enter on a team, then a or d.

🤖 Generated with Claude Code

Approved

Approved. Add (a) and remove (d, y/n) go through the actions layer, which re-checks org admin, so the terminal gates writes like the page. The screen also hides both keys from non-admins. The wording is shared with the page through team_words.ts, and the page text is unchanged. Tests cover add, refusal, declined and confirmed remove, count redraw, and a non-admin. The README is updated. I could not diff against my last-reviewed head because the commit is not in this clone, so I read the full change. No new findings.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs