docs(teams): design of record for platform Teams & community integration #151
Reference in New Issue
Block a user
No description provided.
Delete Branch "docs/teams"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What & why
Adds
website/TEAMS.md— the design of record for Teams as a core platform primitive rather thana mechanism for auto-generating Discord infrastructure. A module stays authoritative for who a Team is
and who belongs to it; core owns everything the platform attaches to it (pages, roster, activity,
forums, notifications), and Discord becomes one optional integration surface among possible others.
Also adds a deferred Teams entry to
android/PLAN.md§ Deferred — recorded with a revival trigger,deliberately not scheduled, since that milestone list is already behind.
Design only. Nothing is implemented, and
MODULE_API.mdremains normative — where the two everdisagree,
MODULE_API.mdwins and this file is wrong.Investigated against the working tree, which corrected three premises of the brief
These are in Part 0, because a plan built on the brief's version would have been wrong:
guild.updatecarries member/online counts, not a roster, and a single leader.servuo-plugins/.../BridgeSocial.csemits integers;module-uo'sshard_guildsmirrors that; thereis no
guild.leaveat all. SogetTeamMembers,getTeamLeaders, the member events, the roster andthe linked/unlinked split have no data source today — which is why a four-repo protocol change
(
PROTOCOL_VERSION3 → 4, addingguild.roster+guild.leave) is the gating phase rather than anafterthought.
ctx.postsis read-only, and bothregisterPostHookandregisterAnnounceLegrun core → module. The activity feed therefore needsits own ingestion member modelled on
ctx.push.publish.module-uo/.../shardBroadcast.js), solive roster status is module-projected through an extension slot rather than a new core transport.
Two constraints that shaped the rest: the bot is a separate container with no
modulesvolume, so amodule physically cannot put a handler in it (hence definition-in-core, execution-dispatched-over-HTTP);
and
notification_subscriptionshas no scope dimension, so Team scoping lives in a computedrecipient set rather than in a stream-per-Team the static catalog could never express.
What's settled
— with the non-contamination invariant (a manual forum grant never writes the membership projection)
and the full RG-account → game identity → membership → external identity binding chain.
{ ok, complete, … }envelopes rather than bare arrays, so no failure shape can be read as "zeroteams", plus a two-strikes quarantine on an authoritative-but-empty answer.
with auto-hide-never-reject and an admin-approval gate scoped to the three actions that publish
untrusted game-sourced strings — not to staff actions generally.
image policy where the author never writes an
<img>tag — core's renderer decides, so the policyis enforceable and flipping it back un-renders every image with no data migration.
a signed liability acknowledgement.
deployment running neither the app nor Discord isn't silently unreachable.
channel-with-overwrites, no role object, no voice channel (MatrixRTC is a session inside a room needing
an SFU the homeserver doesn't ship) and no slash-command registration, so of five interface methods a
Matrix implementation could honestly provide two.
Part 10 — the contract boundary
Added specifically so this isn't ambiguous at implementation time. Every artifact is sorted into
contract / core-internal / public-API / module-owned / wire-protocol, because almost none of this
is contract: the surface is ten members, and the ~15 tables are core-internal and off limits to a
module even though the module is what populates them (
MODULE_API.md§1.2 — table names are notcontract). It also separates the two version numbers in play:
MODULE_API_VERSION1.6.0 (Phase 2) andPROTOCOL_VERSION4 (Phase 1) are independent contracts and neither implies the other.Proposals this makes
MODULE_API_VERSION1.6.0 — minor, additions only;module-uo'scoreApi: "^1.3.0"still resolves.PROTOCOL_VERSION3 → 4, bumped inoverlay.tomlin the same PR as the emitters so the installercan still pair a bundle.
independently shippable. Only Phases 1 → 2 are a hard serial dependency; Phase 3 is a natural stopping
point if a smaller first bet is wanted.
How it was tested
Documentation only — no code, no schema, no routes, so no build, test suite, swagger regeneration or
route manifest is affected.
Verification done while writing:
(
BridgeSocial.cs,shard_guilds,pushDispatch.js,sanitizeHtml.js,csp.js,imageUpload.js,discordManager.js,docker-compose.yml,registries.js,notification_subscriptions).is not a queue-drop risk —
BridgeConfig.QueueCapis 10,000 lines and ~200 guilds is ~200 lines. Thedoc records the real (smaller) concern instead: fat lines against an unbounded
read_line.§x.ycross-references resolve; section and part numbering verified after two rounds ofrestructuring.
recalled.
Reviewed against the repos at
websiteorigin/maine0c961c,module-uomain97e2fdd,linkmain7b65840,servuo-pluginsmainc045bdd.Checklist
AI-assisted contributions (required)
Claude Code (Opus 5). I have reviewed and understandevery change, and take responsibility for it. AI-authored commits are
marked with a
Co-Authored-By/Assisted-Bytrailer.License
(GNU GPL v3.0 or later), and I have the right to contribute it.
🤖 Generated with Claude Code
https://claude.ai/code/session_01WnDSWzpUjw8t8C2hghysNz