Files
CapaKraken/docs/comment-visibility-architecture.md

5.4 KiB

Comment Visibility Architecture

Date: 2026-03-30 Status: Phase 4 mention audience aligned with entity visibility

Problem

The original comment router accepted arbitrary entityType and entityId pairs behind protectedProcedure. That was too broad:

  • comment visibility depends on the backing entity, not on the comment record alone
  • generic strings allowed clients and assistant tools to imply support for entity types that had no explicit policy
  • author/admin checks on resolve and delete were not enough, because list/create access was still effectively "any authenticated user"

Current Product Reality

There are now two real first-party consumers:

  • web UI estimate workspace comments via entityType: "estimate"
  • web UI resource detail comments via entityType: "resource"

The older examples for scope_item, estimate_version, and demand_line remain aspirational, not backed by an explicit visibility model or active UI.

Architecture Decision

Comments now use an explicit entity registry.

  • supported entity types are allowlisted, not free-form
  • each entity type owns:
    • its audience rule
    • its existence check
    • its deep link builder for notifications
  • every comment route calls the entity access layer before touching comment data

Current Policy

Supported entity types:

  • estimate
  • resource

Audience:

  • same audience as the estimate workspace
  • controller, manager, or admin only
  • resource comments inherit the exact resource detail audience:
    • self-service for the caller's own linked resource
    • broad visibility for users who already have resource overview access

Route effects:

  • list, count, create, resolve, and delete all require estimate visibility first
  • listMentionCandidates uses the same entity visibility gate before returning mention candidates
  • the same routes require resource visibility first for entityType: "resource"
  • resolve and delete still require comment author or admin after entity visibility is granted
  • replies are only allowed when the parent comment belongs to the same entity tuple
  • mention notifications use the entity policy link builder instead of hardcoded route assumptions scattered through the router

Why This Shape

  • It closes the real security gap now without pretending a generic multi-entity policy already exists.
  • It keeps future comment expansion additive: a new entity type must be onboarded deliberately.
  • It gives the assistant and UI one source of truth for what is actually supported today.

Phase 2 Foundation

Phase 2 does not add a fake second entity type. Instead, it removes duplication around the real registry:

  • supported comment entity types now live in shared constants instead of being re-declared in multiple layers
  • the API owns a dedicated comment entity registry module for:
    • access checks
    • notification link building
    • assistant-facing supported-entity metadata
  • assistant tool schemas and descriptions derive their comment entity metadata from that registry layer
  • the web comment thread now receives an explicit { entityType, entityId } target instead of hardcoding estimate internally

This keeps the product honest today while making a future second consumer additive rather than copy-paste driven.

Phase 3 Resource Onboarding

resource is now the second deliberate commentable entity because it already had:

  • a real product detail surface
  • an explicit access model in the API
  • a coherent notification deep link target

Implementation shape:

  • shared constants now expose both supported entity types
  • the API registry now maps resource to:
    • existence checks via the backing resource record
    • audience checks via the same resource-read ownership rules as resource.getById
    • notification links via /resources/:id#comments
  • the resource detail page now renders a first-party comment thread instead of leaving the second consumer theoretical
  • assistant comment tools now remain entity-scoped instead of pretending every comment operation is controller-only

Phase 4 Mention Audience Alignment

Mention autocomplete no longer depends on user.listAssignable. Instead, comments own a dedicated mention-candidate route scoped by the same entity tuple as comment read/write access.

Current shape:

  • comment.listMentionCandidates first enforces the entity access policy
  • estimate mention candidates are limited to ADMIN, MANAGER, and CONTROLLER users, matching estimate comment visibility
  • resource mention candidates include:
    • the linked owner of that resource
    • users who already have broad resource visibility through effective permissions
  • user.listAssignable remains a separate operational lookup for assignment flows and is not widened as a side effect of comment support

Extension Rules For Future Entity Types

To add another commentable entity:

  1. Add the entity type to the registry, not just to input examples.
  2. Define the backing audience source of truth.
  3. Add an existence check for that entity.
  4. Add a notification link builder for that entity.
  5. Update assistant tool metadata and assistant visibility gates in the same change.
  6. Add router auth tests for unauthenticated, plain authenticated, and elevated callers.
  7. Update docs/route-access-matrix.md.

Non-Goals

  • generic comment support for arbitrary entities
  • row-level polymorphic authorization based only on entityType strings
  • automatic inheritance for future entities without explicit onboarding