docs(teams): phase 9 as built — roles, not overwrites, and three things §7.3 named that do not exist #163

Merged
whitlocktech merged 1 commits from docs/teams-phase9-voice into edge 2026-08-19 05:15:46 +00:00
Member

Amends TEAMS.md §7.3 inline, marks phase 9 done in Part 12, and adds the
team_integrations row to BACKEND_DESIGN.md's schema table. Pairs with
website#159.

The access model changed shape, and it moves the ceiling. The org lead
settled on a per-Team role always, rather than per-member overwrites escalating
to a role above ~90 members. That deletes voice_overwrite_max, the escalation
and the mode column — but the part worth stating plainly is that the binding
limit moves with it: overwrites are capped per CHANNEL, so the designed shape's
ceiling was "how big can one Team be"; roles are capped per GUILD at 250, so the
built shape's ceiling is "how many Teams can have voice at all". A limit on the
number of Teams is one an operator must be told about before reaching it, so it
is in the admin panel rather than in an error afterwards. The section carries
that as a table.

Three things §7.3 names that the tree does not have, all settled the same
way — by asking the operator, because nothing in the data model can answer:

  • "the staff role" — no staff-role concept exists anywhere. guild_config
    knows a news channel, a modlog channel, an autorole and a filter allowlist,
    and none of them means staff. Now a list of role ids the admin designates,
    empty by default; administrators bypass overwrites anyway, so what was really
    missing is a way to let NON-admin staff in.
  • the parent category's identity — §7.3 says the bot creates it and gives
    the id nowhere to live, since team_integrations.team_id is NOT NULL. It
    lands in settings, written by the server after the bot reports it.
  • whether the bot can act at all — this was assumed and has never been
    checked. The operator invites the bot by hand and no invite URL with a
    permission integer exists in the tree. A preflight is now a precondition:
    enabling is refused 422 while the bot cannot manage channels and roles.

And one number that counts something different from what it says.
voice_min_linked_members becomes teams_voice_min_members, counting every
active member: the question an operator answers with it is "is this Team real
enough", and link state answers a different one.

Two smaller amendments: "recover inside the window → no Discord call made" is
narrowed to no DESTRUCTIVE call (a Team that regained members has members to
grant, and refusing to call at all would leave them outside the channel); and
switching voice off tears nothing down, because a checkbox must not delete
structure in somebody's guild.

Also records what the live rig proved and the two defects it found — a duplicate
result column that made every query fail, and a "Sync now" that reported nothing
had happened while it was happening — plus two problems outside the phase that
it had to work around: swagger-autogen's per-file route limit in
teams.router.js, and a timezone skew between MariaDB's NOW() and JS
Date.now() that moves every staleness judgement, phase 3's public freshness
banner included.


  • AI-assisted: Claude Code (Claude Opus 5)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WnDSWzpUjw8t8C2hghysNz

Amends `TEAMS.md` §7.3 inline, marks phase 9 done in Part 12, and adds the `team_integrations` row to `BACKEND_DESIGN.md`'s schema table. Pairs with **website#159**. **The access model changed shape, and it moves the ceiling.** The org lead settled on a per-Team role always, rather than per-member overwrites escalating to a role above ~90 members. That deletes `voice_overwrite_max`, the escalation and the `mode` column — but the part worth stating plainly is that the binding limit moves with it: overwrites are capped per CHANNEL, so the designed shape's ceiling was "how big can one Team be"; roles are capped per GUILD at 250, so the built shape's ceiling is "how many Teams can have voice at all". A limit on the number of Teams is one an operator must be told about before reaching it, so it is in the admin panel rather than in an error afterwards. The section carries that as a table. **Three things §7.3 names that the tree does not have**, all settled the same way — by asking the operator, because nothing in the data model can answer: - **"the staff role"** — no staff-role concept exists anywhere. `guild_config` knows a news channel, a modlog channel, an autorole and a filter allowlist, and none of them means staff. Now a list of role ids the admin designates, empty by default; administrators bypass overwrites anyway, so what was really missing is a way to let NON-admin staff in. - **the parent category's identity** — §7.3 says the bot creates it and gives the id nowhere to live, since `team_integrations.team_id` is NOT NULL. It lands in settings, written by the server after the bot reports it. - **whether the bot can act at all** — this was assumed and has never been checked. The operator invites the bot by hand and no invite URL with a permission integer exists in the tree. A preflight is now a precondition: enabling is refused 422 while the bot cannot manage channels and roles. **And one number that counts something different from what it says.** `voice_min_linked_members` becomes `teams_voice_min_members`, counting every active member: the question an operator answers with it is "is this Team real enough", and link state answers a different one. Two smaller amendments: "recover inside the window → no Discord call made" is narrowed to no DESTRUCTIVE call (a Team that regained members has members to grant, and refusing to call at all would leave them outside the channel); and switching voice off tears nothing down, because a checkbox must not delete structure in somebody's guild. Also records what the live rig proved and the two defects it found — a duplicate result column that made every query fail, and a "Sync now" that reported nothing had happened while it was happening — plus two problems outside the phase that it had to work around: swagger-autogen's per-file route limit in `teams.router.js`, and a timezone skew between MariaDB's `NOW()` and JS `Date.now()` that moves every staleness judgement, phase 3's public freshness banner included. --- - [x] AI-assisted: Claude Code (Claude Opus 5) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01WnDSWzpUjw8t8C2hghysNz
wtclaude added 1 commit 2026-08-19 05:12:00 +00:00
Amends `TEAMS.md` §7.3 inline, marks phase 9 done in Part 12, and adds the
`team_integrations` row to `BACKEND_DESIGN.md`'s schema table. Pairs with
**website#159**.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech merged commit 6f1f406fe1 into edge 2026-08-19 05:15:46 +00:00
whitlocktech deleted branch docs/teams-phase9-voice 2026-08-19 05:15:46 +00:00
Sign in to join this conversation.
No description provided.