Skip to content

v4.35 — Teams V1.1 — Sub-communities

A team member can now open a room they belong to and read/post to its feed — the last of four sub-community stubs v4.19 honestly gated instead of built, though three of the four turned out to already be shipping elsewhere by the time this milestone was scoped.

Summary

v4.19 Phase 4 closed out the Teams V1 wiring audit by honestly gating eight controls that looked enabled but silently no-opped — an IOU with a ticket number on each one, rather than a control that pretended to work. Four of those eight shared one surface, the team sub-community (room): OBJ-1825 room "Open feed," OBJ-1824 room "Edit," OBJ-1822 the Members-tab room filter, and OBJ-1823 "View team activity." v4.35 set out to pay off all four as a 3-phase milestone.

It shipped as one phase. A mid-milestone scope collision, caught while dispatching Phase 2, found that three of the four source tickets were already shipping independently on master, on a stream this milestone had no visibility into. Kickoff was written at 06:44 CDT; OBJ-1822 merged 67 minutes later (07:51), OBJ-1824 merged 2h21m later (09:05), and OBJ-1823 merged 6h06m later (12:50), while this milestone's own Phase 1 was still in development. All three landed after kickoff, not before it — an earlier version of this page, corrected after review, had the sequence backwards for two of the three. The real gap wasn't the census ref; at the moment of kickoff none of the three tickets existed on any ref yet. It was that nobody re-censused between kickoff and Phase 2's dispatch attempt roughly nine hours later, on a repo where independent streams ship fast. Phases 2 and 3 were cancelled before any executor was dispatched, and the milestone collapsed to Phase 1 — the room feed — plus the integration work needed to land it alongside the independently-shipped work.

The room feed itself was cheap to build because a team sub-community was never a new model: Teams::CreateSubCommunity creates a plain Community row with team_id set, so the entire feed, comment, reaction, and moderation stack already existed. Phase 1 reused /communities/:publicId with an additive team-room mode rather than building a parallel view, and inherited a fresh constraint from CommunityAccessPolicy (shipped independently just before kickoff, closing a live gap where a seated team member could read/post to a gated room by direct API call): the room feed is offered only to members, with Join shown instead on an open room a user hasn't joined yet.

Goal

Tapping "Open feed" on a room the user belongs to opens that room's feed. Archived rooms open read-only for the remainder of their 30-day retention window. The two disabled: true / · soon gates in roomMenuButtons are removed.

.planning/milestones/v4.35-teams-v11-sub-communities-ROADMAP.md, Phase 1 goal

Scope — What Shipped

  • Team-room mode in views/Community.vue, additive and gated strictly on community.teamId presence — public (non-team) community rendering is byte-identical to before, and every pre-existing Community.vue test passed unchanged.
  • Both ⋮ branches wired in TeamSubCommunitiesTab.vue: an active room a member belongs to offers "Open feed"; an archived room a member belongs to offers "Open feed · read-only" with no composer and no reaction/comment affordance; a non-member sees "Join room" on an open room and nothing on a gated one — never a feed entry that would 403.
  • A server-side gap closed that the plan hadn't anticipated: archived rooms could still be posted to server-side even though the client suppressed the composer. Social::CreateCommunityPost gained a require_not_archived guard.
  • Close-out integration landing three independently-shipped tickets alongside Phase 1's own work: master's already-shipped OBJ-1822 (Members-tab room filter, PR #2201), OBJ-1824 (room edit, PR #2207), and OBJ-1823 ("View team activity," PR #2208) were merged into the integration branch and reconciled against Phase 1's changes to the same TeamSubCommunitiesTab.vue file — a semantic conflict in the shared roomMenuButtons computed, not a textual one — before the whole branch promoted to master.
  • docs/architecture/community-authorization.md, a new centralized statement of the OBJ-2302 membership rule and the archived-room write boundary this surface depends on.

Phases

PhaseNameStatusPlansHighlights
1Room feed (OBJ-1825)Shipped3Team-room mode, both ⋮ branches, archived-room posting guard. PR #2210, 27 checks green.
2Room edit (OBJ-1824)Cancelled, supersededShipped independently to master via PR #2207, 2h21m after this milestone's kickoff.
3Sub-community-scoped reads (OBJ-1822 + OBJ-1823)Cancelled, supersededOBJ-1822 shipped independently via PR #2201, 67 min after kickoff. OBJ-1823 shipped independently via PR #2208, 6h06m after kickoff.

Key Decisions

  • A team sub-community is not a new model. Teams::CreateSubCommunity creates a plain, team_id-scoped Community row, so Phase 1 reused the existing feed, comment, reaction, and moderation stack via an additive team-room mode on /communities/:publicId instead of building a parallel view — avoiding duplicating that entire pipeline for zero functional gain.
  • The non-member "open room" state renders Join, never a feed entry that would 403. CommunityAccessPolicy (shipped independently just before this milestone's kickoff) requires an actual CommunityMember record for any team room, closing a prior live gap where a seated team member could read/post to a gated room by direct API call. Rather than loosening that policy to make a "Join CTA from inside the feed" pattern work, Phase 1 decided membership up front from team-query data and offers Join before the feed, not from within it.
  • Checked master's independently-shipped implementations against this milestone's own acceptance criteria, rather than assuming partial coverage. When the scope collision was found, master's OBJ-1824 implementation was traced against the Phase 2 task package's stated risks — including the cross-team lead_is_team_member? guard — and confirmed complete before cancelling the phase, rather than redoing verified work on the strength of "we didn't build this."
  • Collapsed to a 1-phase milestone rather than re-planning Phases 2 and 3 around what had already shipped. A milestone that delivered exactly what it ended up promising is scored against that collapsed scope, not the original plan — recorded explicitly in the ROADMAP and this milestone's audit so a future reader doesn't mistake the collapse for an incomplete milestone.
  • The roomMenuButtons merge was resolved as a semantic conflict, preserving both changes rather than picking one. Phase 1's membership branching and master's real Edit room implementation both needed to land in the same computed property; the close-out merge grafted master's Edit room into both active-room branches where Phase 1 had left a · soon placeholder, rather than letting one side's git-merge auto-resolution silently drop the other's logic.

Requirements Coverage

5 / 5 ROADMAP success criteria satisfied — see the Milestone Audit for the full table. This milestone has no standalone REQUIREMENTS.md; coverage is derived from the ROADMAP's "Success criteria for the milestone" section, per this repo's convention for hand-authored milestones.

CriterionStatus
All four gated controls live, no disabled/· soon/ComingSoonPillSatisfied
docs/product/phase-7-teams.md gating table lists none of the four as gatedSatisfied
No enabled control can read/write a room the user isn't entitled toSatisfied
Public (non-team) communities surface behaviorally unchangedSatisfied
Integration branch promoted, check-branch-promotion.mjs contains exits 0Satisfied

Outcomes

All four sub-community-surface IOUs v4.19 Phase 4 gated are now live: a team member can open a room's feed, read/post to it (or read-only if archived), an owner/admin can edit a room's details without archive-and-recreate, the Members-tab room filter actually filters, and "View team activity" opens a real read-only activity view. Three of the four shipped through a different, unplanned stream — the product outcome is identical to what this milestone set out to deliver, but the delivery record honestly reflects that only the room feed (and the integration work to reconcile it with the rest) came from this milestone's own phases.

Tech Debt

  • (Deliberately out of milestone scope, recorded by the ROADMAP) OBJ-2326 — the parent-team eyebrow needs a small GraphQL field addition.
  • (Deliberately out of milestone scope, documented in community-authorization.md) OBJ-2347 — AddPostComment has no room-access guard. Pre-existing, broader than this milestone (a comment path, not the post path OBJ-2302 fixed).
  • (Adjacent, not this milestone's scope) A fourth v4.19 Phase 4 gated stub, OBJ-1826 (leaderboard scope switcher), shipped independently via PR #2220 while this milestone was in flight — same pattern as the three superseded tickets above, different ticket.
  • (Process gap, not a code defect) At close, OBJ-1825 was found cancelled in the issue tracker while its code was merged and awaiting promotion, and OBJ-1822/OBJ-1823 were in_progress while their code was already on master — both out of sync with reality. Not corrected as part of this milestone's docs close.
  • 3b54bb32b — Phase 1: room feed, team-room mode + honest ⋮ menu gating (PR #2210)
  • b319ad82c — Close-out: merge master into integration branch, resolve roomMenuButtons conflict, promote to master (OBJ-2353)

Last updated: 2026-08-09 · Version: v4.4.5

Loading…