Skip to content

Console: Organization Threads section (list + detail) #67

Description

@rowan-stein

User request

Implement Console feature: Organization → Threads section where an org admin can list and view threads.

Source of truth: agynio/architecture/product/console/console.mdThreads.

Requirements (from spec):

  • Read-only view of all threads in an org.
  • Access:
    • Organization owners
    • Cluster admins (must be granted via OpenFGA model, not hardcoded checks)
  • Org Threads list:
    • Columns: truncated ID, participants (@nicknames), message count, status (active/archived), created date
    • Default sort: newest-first
    • Cursor pagination with “Load more”
  • Thread detail:
    • Participants
    • Paginated messages newest-first
    • Sender @nickname, timestamp, body
    • Attachments as named download links (no mutation)

Decision: No backfill required for pre-existing threads that lack organization_id.


Specification (implementation notes from research)

This feature requires coordinated changes across services/repos.

1) Authorization model (agynio/authorization)

  • Add organization.can_view_threads computed relation:
    • can_view_threads = owner OR admin from cluster
  • Update terraform/model.fga.yaml tests:
    • org owner → can_view_threads = true
    • org member → can_view_threads = false
    • cluster admin → can_view_threads = true

2) Ensure … from cluster works for new orgs (agynio/organizations)

  • On CreateOrganization, write structural tuple:
    • cluster:global#cluster@organization:<orgId>
  • Update rollback logic to delete both tuples when needed.
  • Update ListAccessibleOrganizations to be model-driven but include cluster admins:
    • union results of ListObjects(relation=member) and ListObjects(relation=can_add_member).

3) Threads org-scoping + APIs (agynio/threads + agynio/api + agynio/gateway)

  • DB:
    • Add threads.organization_id (nullable for historical rows; required/populated for new threads)
    • Add threads.message_count (maintained counter; initialized at 0 for new threads)
  • ThreadsService:
    • Require/derive org ID on CreateThread and persist to threads.organization_id
    • Increment message_count on SendMessage
    • Add RPCs:
      • ListOrganizationThreads(organization_id, page_size, page_token, [status])
      • GetThread(thread_id)
    • Add newest-first message pagination support:
      • add explicit order param on GetMessagesRequest (keep existing default to avoid breaking clients)
  • Authorization enforcement (Threads service):
    • ListOrganizationThreads / GetThread must require OpenFGA Check(can_view_threads, organization:<orgId>).

4) Console UI (agynio/console-app)

  • Routes:
    • /organizations/:id/threads (list)
    • /organizations/:id/threads/:threadId (detail)
  • Add Threads nav item under Organization (after Members, before Monitoring).
  • Cursor pagination with “Load more”.
  • Attachments:
    • FilesGateway.GetFileMetadata for name
    • FilesGateway.GetDownloadUrl on click
  • Participant nickname display:
    • Best-effort resolution via existing Users/Agents APIs; expose AppsService.GetAppProfile via Gateway if needed.
    • Fallback to truncated IDs when nickname cannot be resolved.

5) Tests

  • Authorization model tests.
  • Threads service tests:
    • org-scoped list/detail + pagination + authz
    • message pagination newest-first
  • Console: Playwright e2e list→detail→pagination.

Acceptance criteria

  • Org owner can navigate to Org → Threads, see a paginated list, open a thread, and load more messages.
  • Cluster admin can do the same, with permissions coming from OpenFGA (no hardcoded cluster-admin checks in services).
  • Threads list is org-scoped; pre-existing threads without organization_id do not appear.
  • Messages can be paged newest-first per spec.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions