MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-434

6.14.4 Server-side enforcement — exclude a private epic’s children + strip its tells from EVERY public read path (tree / detail child-panel / public board / ready set / search)

Done
Description

Estimate: 70m · Depends on: 6.14.3, 6.12.4

The load-bearing correctness work: for a public/non-member viewer, EXCLUDE a private epic’s children — and STRIP the epic row’s aggregate tells — from EVERY read path, server-side, by extending 6.12’s public PROJECTION. Per 6.14.2. Nothing under a private epic may ever be transmitted to a non-member (no leak in the API / network tab).

  • The single exclusion predicate, in the 6.12.4 projection. Thread ONE "descends from a private epic" predicate into the SAME public projection 6.12 centralises, gated on the viewer being a NON-MEMBER on a public project: when true, a work item whose epic ancestor has the privacy flag set is EXCLUDED from the projected result. Defined once so every read through the projection inherits it — NOT N independent filters. The predicate is an indexable where clause over the parent/ancestor walk (finding #57 — no load-all post-filter).
  • Apply it to every read path (the checklist from 6.14.2): (1) the TREE projection — children of a private epic are absent; (2) the work-item DETAIL child-panel read — the epic’s child list returns EMPTY-with-marker for a non-member; (3) the 6.12 public BOARD read; (4) the 7.0 ready SET read; (5) the 6.1 FilterAST SEARCH compilation (a child of a private epic does NOT match for a public viewer, mirroring GitLab confidential issues being hidden from search). A parameterized shape so adding a new public read without the predicate is caught by 6.14.8.
  • Strip the tells + return the marker. For a private epic ROW in the public projection, OMIT the child count, progress / rollup, and point total, and SET the "children-hidden" marker (per 6.14.2’s DTO decision, e.g. childrenHidden: true) so the UI renders the placeholder without ever receiving a child. The epic title / kind / status stay (the row is a visible placeholder).
  • Members bypass. The predicate is reached ONLY for a non-member viewer on a public project — a project member’s reads return the children + the real rollups unchanged; a non-public project is a no-op.

Stay 4-layer: the predicate + the tell-strip live in the public projection at the service/repository read layer (where 6.12.4 put it), never in a route or the client; no raw Prisma in routes.

Acceptance criteria

  • For a public/non-member viewer, a private epic’s children are ABSENT from the payload of EVERY read path — tree, detail child-panel, public board, ready set, AND FilterAST search — verified at the response level (not the DOM); the predicate is defined ONCE in the 6.12.4 projection (a single auditable branch).
  • The private epic’s public ROW carries title / kind / status + the "children-hidden" marker but OMITS child count, progress / rollup, and point total; the marker lets the UI render the placeholder with no child in the payload.
  • A project MEMBER’s reads return the children + the real rollups unchanged; the flag is a no-op on a non-public project and for a member on a public project.
  • 4-layer respected (predicate + strip in the service/repository projection, no raw Prisma in routes); the exclusion is an indexable where, not a load-all post-filter.

Context refs

  • 6.14.2 (the read-path checklist + the projection-predicate + the tell-strip / marker decision), 6.14.3 (the flag the predicate reads).
  • scripts/plan-seed/data/story-6.12.ts § 6.12.4 (the public PROJECTION this extends — the use-the-real-public-read-subtask-id dependency) + § 6.12.7 (the public roadmap read it also covers).
  • motir-core/lib/services/workItemsService.ts + the tree / detail-child / board / ready-set read paths + the 6.1.1 FilterAST search compiler — the reads the projection predicate threads into.
  • GitLab confidential issues (https://docs.gitlab.com/ee/user/project/issues/confidential_issues.html) — hidden from non-members incl. search, server-side; Jira issue-level security (https://confluence.atlassian.com/adminjiraserver/configuring-issue-level-security-938847117.html) — "not visible anywhere" server-enforced + member/admin bypass.
  • motir-core/CLAUDE.md § 4-layer.