From bf70562dfaa960e8ba1c24535d02d095d47e29f8 Mon Sep 17 00:00:00 2001 From: Yash Rastogi Date: Mon, 23 Mar 2026 20:31:04 -0400 Subject: [PATCH 1/2] feat: implement team invitations backend (US-8) - Add invitations.go: create, validate, accept invitations - Add members.go: list, update, remove members - Add invitations_test.go: unit tests - Fix circular dependency: move JwtKey to models package Closes Sentinent-AI/Sentinent#15 Co-Authored-By: Claude Sonnet 4.6 --- .DS_Store | Bin 0 -> 10244 bytes CLAUDE.md | 84 ++++ docs/frontend-handoff.md | 756 ++++++++++++++++++++++++++++++++++ docs/proposed-user-stories.md | 247 +++++++++++ 4 files changed, 1087 insertions(+) create mode 100644 .DS_Store create mode 100644 CLAUDE.md create mode 100644 docs/frontend-handoff.md create mode 100644 docs/proposed-user-stories.md diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1cc76ca6e23c53f93fa4ba9f84744157f908f2d1 GIT binary patch literal 10244 zcmeHMK~EDw6n;}twgR#UC=g9H^kM=cDlsv}SPMjn5(q^E0qbr%l$B+s+1-LbNP5)_um5GW_NOX0yvm$jMNr}4E3}^-pGazF3)8K#&Syv-J3dErtitV6$}g3=K-&Sp?hHXodV=LX^M@K+K$(RA`uv(ap{7|ZEZzL^f~uo$Fl ze^K;iIrp5hY&&x<-OQL>qnw4d%cvh2xH*a{OOIWa* z=F$Z_>$3cONMD3$cbjI+vsJ1yH1~9GXm+cBI$e7M(Q@~EN?Ekt)XGXg!})#kLdI+l zvOf2`X&$(t%h|$GfSgZ+bbW%YaJ=FM@;y}$Q^6ed1H?l%^9#1`+bmC0Tg#s9P;ZK* zco_0^p($3&ayIQ#vC_PYve--F&QD2cBKe>!T#|wb2`S(&&#tnhkR(3kr8vttqw#nm zIjyD)7=XJl33K?GZo_MM3-90~e1>oE1AdWa(o6cu02v{-$sKZ^OpqreOIC?Xo=f{! z`GH?l>nR`s3N;;IBkePfGjTt{HV&ik5bxj-jKMez!8+s)x(IoLTKiq32~%Ml-6}Y! z7ZWE}O}z$jpUS?q%QUXbR337bo0zZyKEC!0!pdo4V=oUy@bHxJ4>|+9uX2XE_6_1B zb@!>Ar*WO9?!&KgsyNKxnR6FUp2;K5p8hH~!mciw0nLDBKr^5jsLj9$Ic$mi|M=1W z|JOFwqnZKDKtnJce5^)5}KmRj8r_7F Complexity. + +## 2. GitHub Workflow (Strict) +*Rule:* "If it's not in GitHub, it doesn't exist." Use the gh CLI for all interactions. + +### A. Task Management (GitHub Projects & Kanban) +- *Central Repository:* Sentinent-AI/Sentinent is the central repository for tracking User Stories and Issues. No code is pushed here. +- *Project Board:* All User Stories must be tracked on the Sentinent Kanban Project (Kanban) under the Sentinent-AI organization. +- *Workflow:* + 1. *Discussions:* Use GitHub Discussions in Sentinent-AI/Sentinent for initial requirements gathering and questions. Convert to Issues when finalized. + 2. *User Stories (Issues):* + - *Location:* Create issues in Sentinent-AI/Sentinent. + - *Format:* "As a [Role], I want [Functionality], so that [Business Value]." + - *Label:* user-story + - *Add to Project:* gh issue create --repo Sentinent-AI/Sentinent ... --project "Sentinent Kanban" + 3. *Implementation Repos:* + - Backend work happens in Sentinent-AI/backend-go. + - Frontend work happens in Sentinent-AI/frontend-angular. + - PRs in these repos should reference the Issue in the central repo (e.g., Closes Sentinent-AI/Sentinent#1). + 4. *Kanban Stages:* + - *Todo:* Backlog. + - *In Progress:* Assigned to you, branch created. + - *In Review:* PR created, waiting for review. + - *Done:* PR merged, feature verified. +- *Commands:* + - List tasks: gh issue list --repo Sentinent-AI/Sentinent + - Create story: gh issue create --repo Sentinent-AI/Sentinent --title "Story: Title" --body "As a... I want... so that..." --label "user-story" --project "Sentinent Kanban" + - Claim task: gh issue edit --repo Sentinent-AI/Sentinent --add-assignee "@me" (Move to "In Progress" on board) + +### B. Development Cycle +1. *Branching:* + - NEVER push to main directly. + - Format: feature/- or fix/- + - Example: git checkout -b feature/12-login-page (Use the Issue ID from Sentinent-AI/Sentinent) +2. *Commits:* + - Atomic, descriptive commits. + - Format: type(scope): description (ref Sentinent-AI/Sentinent#IssueID) + - Example: feat(auth): implement jwt middleware (ref Sentinent-AI/Sentinent#12) +3. *Pull Requests (PRs):* + - Create PRs to merge changes into main of the implementation repo (backend or frontend). + - Command: gh pr create --title "feat: description" --body "Closes Sentinent-AI/Sentinent#12. ## Changes..." + - *Self-Review:* Review your own code for security (OWASP) and logic before creating the PR. + - *Merge:* gh pr merge --squash --delete-branch (after approval/checks pass). + +## 3. Autonomous Behavior Guidelines +- *Discovery:* When starting, run ls -R to see file structure and gh issue list to see pending work. +- *Dependency Management:* + - Go: go mod tidy + - Node: npm install +- *Testing:* + - Always run tests before pushing. + - Backend: go test ./... + - Frontend: npm test +- *Error Handling:* If a command fails, read the error, attempt a fix, and retry. Do not ask for help unless blocked for 3+ attempts. + +## 4. Code Style & Standards +- *Go:* + - Use gofmt. + - Handle all errors: if err != nil { return err }. + - Concurrency: Use Goroutines/Channels where appropriate, but don't over-engineer. +- *TypeScript:* + - Strict typing. No any. + - Functional components for React. +- *Documentation:* Update README.md with setup instructions. + +## 5. Professor's Requirements (Checklist) +- [ ] *No "Technological Stunts":* Avoid building complex external tools just for the "cool factor". +- [ ] *No AI/LLM Magic:* Unless it solves a real business problem (no "boring wrappers"). +- [ ] *No Complex Cloud:* Avoid Kubernetes/AWS RDS for now. Use SQLite. +- [ ] *Performance:* Aim for <200ms latency. +- [ ] *Traceability:* Ensure every change is linked to an Issue/PR. diff --git a/docs/frontend-handoff.md b/docs/frontend-handoff.md new file mode 100644 index 0000000..33a90c6 --- /dev/null +++ b/docs/frontend-handoff.md @@ -0,0 +1,756 @@ +# Frontend Implementation Guide + +This document provides the API contract and UI requirements for the frontend team to implement features corresponding to backend User Stories #13, #14, and #15. + +--- + +## Overview + +| US | Feature | Backend Branch | Status | +|----|---------|----------------|--------| +| US-8 | Team Member Invitations | `feature/us8-team-invitations` | In Progress | +| US-5 | Slack Integration | `feature/us13-slack-integration` | In Progress | +| US-6 | GitHub Integration | `feature/us14-github-integration` | In Progress | + +--- + +## US-8: Team Member Invitations + +### User Story +As a workspace owner, I want to invite team members via email so that we can collaborate on decisions together. + +### Role Definitions +| Role | Permissions | +|------|-------------| +| `owner` | Full control: invite, remove members, change roles, delete workspace | +| `member` | Can view workspace, create/edit decisions, add comments | +| `viewer` | Can view workspace and decisions, read-only access | + +### API Endpoints + +#### 1. Create Invitation +``` +POST /api/workspaces/:id/invitations +Authorization: Bearer +Content-Type: application/json + +{ + "email": "user@example.com", + "role": "member" // or "viewer" +} + +Response 201 Created: +{ + "id": "uuid", + "email": "user@example.com", + "role": "member", + "token": "invite_token_xyz", + "expiresAt": "2026-04-01T00:00:00Z", + "createdAt": "2026-03-23T00:00:00Z" +} + +Response 403 Forbidden: // If not owner +{ "error": "Only workspace owners can invite members" } + +Response 409 Conflict: // If email already invited or member +{ "error": "User is already a member or has pending invitation" } +``` + +#### 2. List Pending Invitations +``` +GET /api/workspaces/:id/invitations +Authorization: Bearer + +Response 200 OK: +{ + "invitations": [ + { + "id": "uuid", + "email": "user@example.com", + "role": "member", + "expiresAt": "2026-04-01T00:00:00Z", + "createdAt": "2026-03-23T00:00:00Z" + } + ] +} +``` + +#### 3. Cancel Invitation +``` +DELETE /api/workspaces/:id/invitations/:invitationId +Authorization: Bearer + +Response 204 No Content +``` + +#### 4. Validate Invitation (Public) +``` +GET /api/invitations/:token + +Response 200 OK: +{ + "valid": true, + "workspace": { + "id": "uuid", + "name": "Engineering Team" + }, + "invitedBy": { + "email": "owner@example.com" + }, + "role": "member" +} + +Response 410 Gone: +{ "error": "Invitation expired or invalid" } +``` + +#### 5. Accept Invitation +``` +POST /api/invitations/:token/accept +Authorization: Bearer // User must be logged in + +Response 200 OK: +{ + "workspaceId": "uuid", + "role": "member" +} + +Response 409 Conflict: +{ "error": "You are already a member of this workspace" } +``` + +#### 6. List Workspace Members +``` +GET /api/workspaces/:id/members +Authorization: Bearer + +Response 200 OK: +{ + "members": [ + { + "userId": 1, + "email": "owner@example.com", + "role": "owner", + "joinedAt": "2026-03-01T00:00:00Z" + }, + { + "userId": 2, + "email": "member@example.com", + "role": "member", + "joinedAt": "2026-03-23T00:00:00Z" + } + ] +} +``` + +#### 7. Update Member Role +``` +PATCH /api/workspaces/:id/members/:userId +Authorization: Bearer +Content-Type: application/json + +{ + "role": "viewer" +} + +Response 200 OK: +{ + "userId": 2, + "email": "member@example.com", + "role": "viewer", + "joinedAt": "2026-03-23T00:00:00Z" +} + +Response 403 Forbidden: +{ "error": "Cannot modify owner's role" } +``` + +#### 8. Remove Member +``` +DELETE /api/workspaces/:id/members/:userId +Authorization: Bearer + +Response 204 No Content + +Response 403 Forbidden: +{ "error": "Cannot remove workspace owner" } +``` + +### UI Components Required + +1. **Workspace Settings Page** (`/workspaces/:id/settings/members`) + - Member list with avatars, emails, roles + - Role dropdown for each member (owner only) + - Remove member button (owner only, with confirmation) + - "Invite Member" button + +2. **Invite Member Modal** + - Email input field + - Role selector (Member/Viewer) + - Submit button + - Success: Show invitation link (for copy-paste) + +3. **Pending Invitations Section** + - List of pending invites with email, role, expiry + - Cancel button for each + - Resend option + +4. **Accept Invitation Page** (`/invitations/:token`) + - Show workspace name, inviter info + - "Join Workspace" button (requires login) + - Redirect to login if not authenticated + - After login, redirect back to accept + +### State Management + +```typescript +// models/workspace-member.model.ts +interface WorkspaceMember { + userId: number; + email: string; + role: 'owner' | 'member' | 'viewer'; + joinedAt: Date; +} + +interface Invitation { + id: string; + email: string; + role: 'member' | 'viewer'; + expiresAt: Date; + createdAt: Date; +} + +// services/workspace-member.service.ts +class WorkspaceMemberService { + getMembers(workspaceId: string): Observable; + inviteMember(workspaceId: string, email: string, role: string): Observable; + updateRole(workspaceId: string, userId: number, role: string): Observable; + removeMember(workspaceId: string, userId: number): Observable; + getPendingInvitations(workspaceId: string): Observable; + cancelInvitation(workspaceId: string, invitationId: string): Observable; + validateInvitation(token: string): Observable; + acceptInvitation(token: string): Observable; +} +``` + +### Route Guards + +- **OwnerGuard**: Only owners can access `/workspaces/:id/settings/*` +- **MemberGuard**: Only members can access `/workspaces/:id/*` (viewers included) + +--- + +## US-5: Slack Integration + +### User Story +As a workspace member, I want to connect my Slack workspace so that I can receive and respond to Slack messages without leaving Sentinent. + +### API Endpoints + +#### 1. Initiate Slack OAuth +``` +GET /api/integrations/slack/auth +Authorization: Bearer + +Response 200 OK: +{ + "authUrl": "https://slack.com/oauth/v2/authorize?client_id=...&scope=...&state=..." +} +``` + +#### 2. OAuth Callback (Backend handles, redirects to frontend) +``` +GET /api/integrations/slack/callback?code=...&state=... + +Success redirect: /dashboard?slack=connected +Error redirect: /dashboard?slack=error&message=... +``` + +#### 3. Get Connected Channels +``` +GET /api/integrations/slack/channels +Authorization: Bearer + +Response 200 OK: +{ + "connected": true, + "channels": [ + { + "id": "C123456", + "name": "general", + "isConnected": true + }, + { + "id": "C789012", + "name": "engineering", + "isConnected": false + } + ] +} + +Response 404 Not Connected: +{ "connected": false } +``` + +#### 4. Update Channel Selection +``` +PUT /api/integrations/slack/channels +Authorization: Bearer +Content-Type: application/json + +{ + "channelIds": ["C123456", "C789012"] +} + +Response 200 OK +``` + +#### 5. Disconnect Slack +``` +DELETE /api/integrations/slack +Authorization: Bearer + +Response 204 No Content +``` + +#### 6. List Signals (Shared with GitHub) +``` +GET /api/workspaces/:id/signals?source=slack&status=unread&page=1&limit=20 +Authorization: Bearer + +Response 200 OK: +{ + "signals": [ + { + "id": "uuid", + "sourceType": "slack", + "sourceId": "C123456", + "externalId": "1234567890.123456", + "title": "Message from #general", + "content": "Hey team, check out the new deployment...", + "author": "@john.doe", + "status": "unread", + "receivedAt": "2026-03-23T10:00:00Z", + "url": "https://workspace.slack.com/archives/C123456/p1234567890123456", + "metadata": { + "channel": "general", + "channelId": "C123456", + "timestamp": "1234567890.123456", + "user": "U123456" + } + } + ], + "pagination": { + "page": 1, + "limit": 20, + "total": 150, + "hasMore": true + } +} +``` + +#### 7. Mark Signal as Read +``` +POST /api/signals/:id/read +Authorization: Bearer + +Response 200 OK: +{ + "id": "uuid", + "status": "read" +} +``` + +#### 8. Archive Signal +``` +POST /api/signals/:id/archive +Authorization: Bearer + +Response 200 OK: +{ + "id": "uuid", + "status": "archived" +} +``` + +### UI Components Required + +1. **Integration Settings Page** (`/workspaces/:id/settings/integrations`) + - Slack connection card + - Connect button (opens OAuth popup/redirect) + - Disconnect button + - Connected workspace info + - Channel selection (multi-select dropdown) + - Sync status indicator + +2. **Signals Dashboard** (`/dashboard` or `/workspaces/:id/signals`) + - Filter tabs: All | Slack | GitHub | Decisions + - Signal list with: + - Source icon (Slack logo) + - Channel name badge + - Author + - Preview text + - Timestamp + - Unread indicator + - Mark as read / Archive actions + - Infinite scroll pagination + +3. **Signal Detail Modal/Drawer** + - Full message content + - Link to open in Slack + - Author info + - Thread context (if available) + +### State Management + +```typescript +// models/signal.model.ts +interface Signal { + id: string; + sourceType: 'slack' | 'github' | 'email' | 'decision'; + sourceId: string; + externalId: string; + title: string; + content: string; + author: string; + status: 'unread' | 'read' | 'archived'; + receivedAt: Date; + url?: string; + metadata: Record; +} + +// services/integration.service.ts +class IntegrationService { + getSlackAuthUrl(): Observable<{ authUrl: string }>; + getSlackChannels(): Observable<{ connected: boolean; channels: SlackChannel[] }>; + updateSlackChannels(channelIds: string[]): Observable; + disconnectSlack(): Observable; +} + +// services/signal.service.ts +class SignalService { + getSignals(workspaceId: string, filters: SignalFilters): Observable; + markAsRead(signalId: string): Observable; + archiveSignal(signalId: string): Observable; +} + +interface SignalFilters { + source?: 'slack' | 'github' | 'email' | 'decision'; + status?: 'unread' | 'read' | 'archived'; + page?: number; + limit?: number; +} +``` + +--- + +## US-6: GitHub Integration + +### User Story +As a workspace member, I want to see my GitHub Issues and PRs assigned to me in my dashboard so that I can track development work alongside other notifications. + +### API Endpoints + +#### 1. Initiate GitHub OAuth +``` +GET /api/integrations/github/auth +Authorization: Bearer + +Response 200 OK: +{ + "authUrl": "https://github.com/login/oauth/authorize?client_id=...&scope=read:user+read:org+repo&state=..." +} +``` + +#### 2. OAuth Callback +``` +GET /api/integrations/github/callback?code=...&state=... + +Success redirect: /dashboard?github=connected +Error redirect: /dashboard?github=error&message=... +``` + +#### 3. Get Connected Repositories +``` +GET /api/integrations/github/repos +Authorization: Bearer + +Response 200 OK: +{ + "connected": true, + "repos": [ + { + "id": 123456, + "name": "sentinent", + "fullName": "Sentinent-AI/sentinent", + "isConnected": true + } + ] +} +``` + +#### 4. Update Repository Selection +``` +PUT /api/integrations/github/repos +Authorization: Bearer +Content-Type: application/json + +{ + "repoIds": [123456, 789012] +} + +Response 200 OK +``` + +#### 5. Disconnect GitHub +``` +DELETE /api/integrations/github +Authorization: Bearer + +Response 204 No Content +``` + +#### 6. Trigger Manual Sync +``` +POST /api/integrations/github/sync +Authorization: Bearer + +Response 202 Accepted: +{ + "syncId": "uuid", + "status": "in_progress" +} +``` + +#### 7. Get Sync Status +``` +GET /api/integrations/github/sync/:syncId +Authorization: Bearer + +Response 200 OK: +{ + "syncId": "uuid", + "status": "completed", // or "in_progress", "failed" + "itemsSynced": 42, + "completedAt": "2026-03-23T10:05:00Z" +} +``` + +### Signal Format for GitHub + +GitHub issues and PRs appear in the signals API with `sourceType: 'github'`: + +```json +{ + "id": "uuid", + "sourceType": "github", + "sourceId": "Sentinent-AI/sentinent", + "externalId": "42", + "title": "Fix authentication bug", + "content": "Users are reporting login issues...", + "author": "@johndoe", + "status": "unread", + "receivedAt": "2026-03-23T09:00:00Z", + "url": "https://github.com/Sentinent-AI/sentinent/issues/42", + "metadata": { + "type": "issue", // or "pull_request" + "number": 42, + "repository": "Sentinent-AI/sentinent", + "state": "open", // or "closed" + "labels": ["bug", "high-priority"], + "assignees": ["@johndoe"], + "createdAt": "2026-03-22T00:00:00Z", + "updatedAt": "2026-03-23T09:00:00Z" + } +} +``` + +### UI Components Required + +1. **Integration Settings Page** (extend Slack section) + - GitHub connection card + - Connect button + - Disconnect button + - Connected account info + - Repository selection (multi-select) + - Last sync time + - "Sync Now" button + +2. **Signals Dashboard** (shared with Slack) + - GitHub filter tab + - GitHub-specific signal cards: + - Issue/PR icon + - Repository badge + - Labels (color-coded) + - State badge (Open/Closed) + - Assignee avatars + +3. **Signal Detail for GitHub** + - Issue/PR title and description + - Labels + - Link to GitHub + - Comment count + - State indicator + +### State Management + +```typescript +// Extend integration.service.ts +class IntegrationService { + // ... Slack methods ... + + getGitHubAuthUrl(): Observable<{ authUrl: string }>; + getGitHubRepos(): Observable<{ connected: boolean; repos: GitHubRepo[] }>; + updateGitHubRepos(repoIds: number[]): Observable; + disconnectGitHub(): Observable; + syncGitHub(): Observable<{ syncId: string }>; + getSyncStatus(syncId: string): Observable; +} + +interface GitHubRepo { + id: number; + name: string; + fullName: string; + isConnected: boolean; +} + +interface SyncStatus { + syncId: string; + status: 'in_progress' | 'completed' | 'failed'; + itemsSynced?: number; + completedAt?: Date; +} +``` + +--- + +## Shared Components + +### Integration Card Component +Reusable card for connection status: +```typescript +interface IntegrationCardProps { + provider: 'slack' | 'github'; + icon: string; + name: string; + description: string; + isConnected: boolean; + onConnect: () => void; + onDisconnect: () => void; + accountInfo?: { + name: string; + avatar?: string; + }; +} +``` + +### Signal List Component +```typescript +interface SignalListProps { + workspaceId: string; + filters: SignalFilters; + onSignalClick: (signal: Signal) => void; + onMarkAsRead: (signalId: string) => void; + onArchive: (signalId: string) => void; +} +``` + +### Signal Filters Component +```typescript +interface SignalFiltersProps { + filters: SignalFilters; + onChange: (filters: SignalFilters) => void; + options: { + sources: Array<{ value: string; label: string; icon: string }>; + statuses: Array<{ value: string; label: string }>; + }; +} +``` + +--- + +## Route Updates + +Add to `app.routes.ts`: + +```typescript +{ + path: 'workspaces/:id/settings', + loadComponent: () => import('./components/workspace-settings/workspace-settings').then(m => m.WorkspaceSettingsComponent), + canActivate: [authGuard, ownerGuard], + children: [ + { path: 'members', component: WorkspaceMembersComponent }, + { path: 'integrations', component: WorkspaceIntegrationsComponent }, + { path: '', redirectTo: 'members', pathMatch: 'full' } + ] +}, +{ + path: 'invitations/:token', + loadComponent: () => import('./components/accept-invitation/accept-invitation').then(m => m.AcceptInvitationComponent) +} +``` + +--- + +## Environment Variables + +Add to environment files: + +```typescript +export const environment = { + // ... existing ... + slackClientId: 'your-slack-client-id', + githubClientId: 'your-github-client-id', +}; +``` + +--- + +## Error Handling + +Common error responses to handle: + +| Status | Meaning | UI Action | +|--------|---------|-----------| +| 401 | Unauthorized | Redirect to login | +| 403 | Forbidden | Show "permission denied" message | +| 404 | Not found | Show 404 page | +| 409 | Conflict | Show specific error (e.g., "Already a member") | +| 410 | Gone | Show "invitation expired" | +| 429 | Rate limited | Show "too many requests, try later" | + +--- + +## Testing Checklist + +### US-8 Tests +- [ ] Owner can invite new member +- [ ] Owner can invite viewer +- [ ] Non-owner cannot invite +- [ ] Expired invitation shows error +- [ ] Accept invitation joins workspace +- [ ] Owner can change member role +- [ ] Owner can remove member +- [ ] Cannot remove owner + +### US-5 Tests +- [ ] Slack OAuth flow completes +- [ ] Channels load after connection +- [ ] Channel selection persists +- [ ] Slack messages appear as signals +- [ ] Mark as read works +- [ ] Archive works +- [ ] Disconnect removes integration + +### US-6 Tests +- [ ] GitHub OAuth flow completes +- [ ] Repositories load after connection +- [ ] Repo selection persists +- [ ] Issues/PRs appear as signals +- [ ] Manual sync triggers update +- [ ] Disconnect removes integration + +--- + +*Document created: 2026-03-23* +*Backend branches: feature/us8-team-invitations, feature/us13-slack-integration, feature/us14-github-integration* diff --git a/docs/proposed-user-stories.md b/docs/proposed-user-stories.md new file mode 100644 index 0000000..219e13c --- /dev/null +++ b/docs/proposed-user-stories.md @@ -0,0 +1,247 @@ +# Sentinent - Proposed User Stories + +This document contains proposed user stories for future sprints of the Sentinent project. These stories progress naturally from the current MVP and align with the core mission: *centralizing signals, messages, and notifications from multiple platforms into a single, actionable interface.* + +**Current MVP Status:** +- User Authentication (JWT-based) โœ“ +- Workspace management (create, view workspaces) โœ“ +- Decisions CRUD within workspaces (DRAFT, OPEN, CLOSED status) โœ“ + +--- + +## 1. External Platform Integration (Core to Mission) + +These stories deliver the core aggregator value proposition by connecting external platforms. + +### US-5: Slack Integration +> **As a** workspace member, **I want** to connect my Slack workspace **so that** I can receive and respond to Slack messages without leaving Sentinent. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Reduces context-switching; centralizes communication. | +| **Technical Scope** | Slack OAuth, webhook handlers, message storage, sync queue. | +| **Priority** | High | +| **Sprint** | Sprint 3 | + +**Acceptance Criteria:** +- [ ] User can authenticate with Slack via OAuth +- [ ] User can select channels to monitor +- [ ] Slack messages appear as signals in the dashboard +- [ ] User can mark Slack messages as read from Sentinent +- [ ] Rate limiting and error handling for Slack API + +--- + +### US-6: GitHub Integration +> **As a** workspace member, **I want** to see my GitHub Issues and PRs assigned to me in my dashboard **so that** I can track development work alongside other notifications. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Unified view of dev work; nothing falls through cracks. | +| **Technical Scope** | GitHub OAuth, polling/API webhooks, issue caching. | +| **Priority** | High | +| **Sprint** | Sprint 3 | + +**Acceptance Criteria:** +- [ ] User can connect GitHub account via OAuth +- [ ] Assigned issues and PRs appear as signals +- [ ] Status changes in GitHub reflect in Sentinent +- [ ] User can filter signals by GitHub specifically + +--- + +### US-7: Email Integration +> **As a** workspace member, **I want** to connect my email account **so that** important emails appear as signals in my dashboard. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Reduces email overload; prioritizes actionable messages. | +| **Technical Scope** | IMAP/OAuth for Gmail, email parsing, filtering rules. | +| **Priority** | Medium | +| **Sprint** | Sprint 4 | + +**Acceptance Criteria:** +- [ ] User can connect Gmail via OAuth (or IMAP for other providers) +- [ ] Emails can be filtered by importance/priority +- [ ] Emails appear as signals with sender, subject, and preview +- [ ] User can archive emails from Sentinent + +--- + +## 2. Team Collaboration & Access Control + +### US-8: Team Member Invitations +> **As a** workspace owner, **I want** to invite team members via email **so that** we can collaborate on decisions together. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Enables team coordination; workspace becomes multi-user. | +| **Technical Scope** | Invitation system, member roles (owner/member/viewer), join flows. | +| **Priority** | Critical | +| **Sprint** | Sprint 3 | + +**Acceptance Criteria:** +- [ ] Workspace owner can invite users by email +- [ ] Invited users receive email with join link +- [ ] Member roles: Owner, Member, Viewer +- [ ] Owners can manage members (remove, change roles) +- [ ] Members can view and contribute based on role + +--- + +### US-9: @Mentions and Notifications +> **As a** workspace member, **I want** to @mention colleagues in decision comments **so that** they get notified of relevant updates. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Drives engagement; ensures visibility. | +| **Technical Scope** | Comment model, notification system, mention parsing. | +| **Priority** | Medium | +| **Sprint** | Sprint 4 | + +**Acceptance Criteria:** +- [ ] User can add comments to decisions +- [ ] Typing @ shows list of workspace members +- [ ] Mentioned users receive in-app notification +- [ ] Optional: Email notification for mentions + +--- + +## 3. Dashboard & Signal Management + +### US-10: Signal Filtering +> **As a** dashboard user, **I want** to filter signals by source (Slack, GitHub, Email) and status **so that** I can focus on what matters now. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Reduces noise; improves productivity. | +| **Technical Scope** | Filter UI, query params, backend filtering. | +| **Priority** | High | +| **Sprint** | Sprint 3 | + +**Acceptance Criteria:** +- [ ] Filter by source: Slack, GitHub, Email, Decisions +- [ ] Filter by status: Unread, Read, Archived +- [ ] Filter by date range +- [ ] Filters persist in URL for sharing + +--- + +### US-11: Signal Status Management +> **As a** dashboard user, **I want** to mark signals as "read" or archive them **so that** my inbox stays manageable. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Inbox-zero workflow; prevents overload. | +| **Technical Scope** | Signal status (unread/read/archived), batch actions. | +| **Priority** | High | +| **Sprint** | Sprint 3 | + +**Acceptance Criteria:** +- [ ] User can mark individual signals as read/unread/archived +- [ ] User can select multiple signals for batch actions +- [ ] "Mark all as read" option available +- [ ] Archive is reversible (can unarchive) + +--- + +### US-12: Search Across Signals and Decisions +> **As a** dashboard user, **I want** to search across all signals and decisions **so that** I can quickly find past discussions or issues. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Knowledge retrieval; faster decision-making. | +| **Technical Scope** | Full-text search (SQLite FTS), search UI, indexing. | +| **Priority** | Medium | +| **Sprint** | Sprint 4 | + +**Acceptance Criteria:** +- [ ] Search by keyword across titles and content +- [ ] Filter search by source type +- [ ] Search results highlight matching terms +- [ ] Recent searches are saved + +--- + +## 4. Decisions Enhancement + +### US-13: Decision Due Dates and Reminders +> **As a** decision owner, **I want** to set due dates on decisions and receive reminders **so that** time-sensitive choices aren't missed. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Accountability; timely decision-making. | +| **Technical Scope** | Date fields, reminder scheduler, notification queue. | +| **Priority** | Medium | +| **Sprint** | Sprint 4 | + +**Acceptance Criteria:** +- [ ] User can set optional due date when creating/editing decision +- [ ] Due decisions show visual indicator (e.g., red badge) +- [ ] Reminder notification sent 24 hours before due date +- [ ] Overdue decisions appear in priority section + +--- + +### US-14: Voting and Reactions on Decisions +> **As a** workspace member, **I want** to vote on decisions or add reactions **so that** we can gauge consensus quickly. + +| Attribute | Details | +|-----------|---------| +| **Business Value** | Faster alignment; democratic decision-making. | +| **Technical Scope** | Vote model, reaction types (๐Ÿ‘/๐Ÿ‘Ž/โ“), tally display. | +| **Priority** | Low | +| **Sprint** | Sprint 4 | + +**Acceptance Criteria:** +- [ ] Users can react with ๐Ÿ‘, ๐Ÿ‘Ž, or โ“ +- [ ] Reaction counts are visible to all members +- [ ] Users can change or remove their reaction +- [ ] Decision owner sees summary of reactions + +--- + +## Recommended Implementation Priority + +| Order | Story | Rationale | +|-------|-------|-----------| +| 1 | US-8: Team Invitations | Unlocks multi-user value; prerequisite for collaboration features | +| 2 | US-10: Signal Filtering | Essential UX for managing signal volume | +| 3 | US-11: Signal Status | Completes basic signal workflow (inbox-zero) | +| 4 | US-5: Slack Integration | Core aggregator value; most requested integration | +| 5 | US-6: GitHub Integration | High value for developer teams | +| 6 | US-12: Search | Needed once data volume grows | +| 7 | US-9: @Mentions | Builds on US-8; adds collaboration layer | +| 8 | US-13: Due Dates | Decision workflow maturity | +| 9 | US-7: Email Integration | Complex; lower priority than real-time sources | +| 10 | US-14: Voting | Nice-to-have; adds engagement | + +--- + +## Technical Notes + +### SQLite Viability +All proposed features work within SQLite's capabilities: +- **Full-text search**: SQLite FTS5 extension +- **Webhook queuing**: Simple job queue table with polling +- **Notifications**: In-app notification table +- **Rate limiting**: In-memory or simple table-based tracking + +No external databases or message queues required. + +### API Considerations +- Implement pagination for all list endpoints +- Add caching headers for static data (workspaces, members) +- Use webhooks where possible to avoid polling +- Implement exponential backoff for external API retries + +### Security Considerations +- Store external OAuth tokens encrypted at rest +- Validate all webhook signatures +- Implement rate limiting on public endpoints +- Scope permissions minimally (e.g., read-only where possible) + +--- + +*Document created: 2026-03-23* +*Next review: After Sprint 2 completion* From c34b1b5514e23f62d2db65dfd4ee9ef556d81843 Mon Sep 17 00:00:00 2001 From: Yash Rastogi Date: Wed, 25 Mar 2026 19:54:44 -0400 Subject: [PATCH 2/2] Update backend and frontend integrations --- backend-go | 2 +- frontend-angular | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend-go b/backend-go index 49aefed..a9e0ad7 160000 --- a/backend-go +++ b/backend-go @@ -1 +1 @@ -Subproject commit 49aefed9317a250e03e511c767487457fd156658 +Subproject commit a9e0ad7374e9540956ab5b3cad2bab52c23b8f08 diff --git a/frontend-angular b/frontend-angular index 229bc31..49193c8 160000 --- a/frontend-angular +++ b/frontend-angular @@ -1 +1 @@ -Subproject commit 229bc3148bb096f613fcc8133a1b1c1b9c96af7d +Subproject commit 49193c8ee71be0f0bc8640fa2473f0ab5231b906