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.md → Threads.
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.
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.md→ Threads.Requirements (from spec):
@nicknames), message count, status (active/archived), created date@nickname, timestamp, bodyDecision: 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)organization.can_view_threadscomputed relation:can_view_threads = owner OR admin from clusterterraform/model.fga.yamltests:can_view_threads = truecan_view_threads = falsecan_view_threads = true2) Ensure
… from clusterworks for new orgs (agynio/organizations)CreateOrganization, write structural tuple:cluster:global#cluster@organization:<orgId>ListAccessibleOrganizationsto be model-driven but include cluster admins:ListObjects(relation=member)andListObjects(relation=can_add_member).3) Threads org-scoping + APIs (
agynio/threads+agynio/api+agynio/gateway)threads.organization_id(nullable for historical rows; required/populated for new threads)threads.message_count(maintained counter; initialized at 0 for new threads)CreateThreadand persist tothreads.organization_idmessage_countonSendMessageListOrganizationThreads(organization_id, page_size, page_token, [status])GetThread(thread_id)GetMessagesRequest(keep existing default to avoid breaking clients)ListOrganizationThreads/GetThreadmust require OpenFGACheck(can_view_threads, organization:<orgId>).4) Console UI (
agynio/console-app)/organizations/:id/threads(list)/organizations/:id/threads/:threadId(detail)FilesGateway.GetFileMetadatafor nameFilesGateway.GetDownloadUrlon clickAppsService.GetAppProfilevia Gateway if needed.5) Tests
Acceptance criteria
organization_iddo not appear.