Files
gitlore/PRD.md
teernisse 7702d2a493 initial
2026-01-20 13:11:40 -05:00

18 KiB

GitLab Inbox - Product Requirements Document

Overview

Product Name: GitLab Inbox Version: 1.0 Author: Taylor Eernisse Date: January 16, 2026

Problem Statement

Managing GitLab activity with ADHD is overwhelming. The native GitLab interface creates cognitive overload through:

  • Information scatter: Issues, MRs, and activity are spread across multiple pages
  • Missing reply awareness: Hard to know when someone has responded to your question (not fully covered by /todos alone)
  • Context loss: Difficult to find the right tab or remember which conversation you were tracking
  • No unified "what's next": Multiple clicks required to understand what needs attention

Solution

A local, always-open "inbox" application that presents GitLab notifications in an ADHD-friendly interface with explicit "handled" tracking, snooze capabilities, watchlist for awaiting replies, and progress visibility.


Target User

Primary Persona: Software developer with ADHD working on 1-2 GitLab projects who needs to track conversations and respond to mentions, reviews, and assignments without cognitive overload.

Key Characteristics:

  • Needs clear "what's next" visibility
  • Benefits from external accountability (seeing who's waiting)
  • Motivated by progress tracking (watching a list shrink)
  • Prefers always-open tools over on-demand checks
  • Struggles with context switching and finding the right place
  • Needs a "not now but not forgotten" path that doesn't require willpower

Goals

User Goals

  1. Know immediately when someone has replied or needs my attention
  2. Quickly navigate to the right place in GitLab to respond
  3. Track what I've handled today for satisfaction and progress awareness
  4. Reduce cognitive load of manually tracking conversations
  5. Defer items temporarily without losing accountability (snooze)
  6. Know when someone has replied to something I'm waiting on

Product Goals

  1. Reduce time-to-awareness for GitLab notifications
  2. Eliminate the need to manually poll GitLab for updates
  3. Provide ADHD-friendly UX patterns (clear actions, progress visibility, minimal decisions)
  4. Enable keyboard-first operation to reduce friction

Non-Goals (v1.0)

  • Replacing GitLab for any write operations (commenting, reviewing, merging)
  • Supporting multiple GitLab instances
  • Team/shared usage
  • Mobile support

Core Features

1. Inbox View (Primary)

Description: Display all GitLab todos (notifications) that need attention.

Data Source: GitLab /todos API endpoint

Display Elements (per item):

Element Description
Action Badge Type indicator: mentioned, assigned, review_requested, build_failed, etc.
Target Title MR or Issue title
Author Who triggered this todo (name + avatar)
Time Relative time since created ("2h ago", "3 days")
Project Project name for context

Interactions:

  • Click item / Enter → Opens target URL in browser (GitLab)
  • Mark Handled → Moves item to Done Today (local state only)
  • Snooze → Hides item until a chosen time (local state only)
  • DismissPOST /todos/:id/mark_as_done (marks as done in GitLab)

Filtering: Items marked as "handled" or "snoozed" locally are hidden from Inbox.

2. Snoozed View

Description: Items temporarily deferred until their wake time.

Purpose:

  • "Not now but not forgotten" path
  • Reduces inbox dread by shrinking the visible list
  • Enables focus sessions: clear the deck, then pull from Snoozed intentionally

Snooze Options:

  • Later today (3 hours)
  • Tomorrow morning (9am local)
  • Next weekday (Mon-Fri, 9am)
  • Custom date/time

Behavior:

  • Snoozed items are hidden from Inbox
  • When wake time passes, item returns to Inbox with a "Woke up" indicator
  • Snoozed view shows all snoozed items with their wake times

3. Watchlist (Awaiting Reply)

Description: Targets you're explicitly waiting on (MRs/Issues/etc.). Alerts when there is new activity since you last checked.

Purpose:

  • GitLab todos don't guarantee "someone replied" notifications
  • Explicit watch semantics for "I'm waiting on Bob" tracking
  • Gain "external accountability" symmetry

Data Sources:

  • Primary: /todos (fast path for items that generate new todos)
  • Secondary: per-target updated_at/notes polling for watched items (small set)

Interactions:

  • Mark Handled → optionally "Add to Watchlist" toggle
  • Watch item shows "Last seen" timestamp and "New activity" indicator
  • Click to open target in GitLab
  • Remove from watchlist when no longer waiting

4. Done Today View

Description: Items marked as handled during the current day.

Purpose:

  • ADHD-friendly progress visibility
  • Satisfaction from watching list shrink
  • Review of daily accomplishments

Behavior:

  • Stored as date-bucketed ledger keyed by local date (YYYY-MM-DD)
  • "Done Today" shows bucket for current local date
  • Option to clear today's bucket only
  • Historical buckets retained for potential "Done Yesterday" or weekly views

5. Manual Refresh

Description: Button to fetch latest todos on demand.

Purpose: Immediate update when user knows something changed.

6. Background Polling (v1.1)

Description: Automatic periodic refresh of todos.

Configuration:

  • Base interval (default: 60s)
  • Backoff on failure (exponential, capped at 15m) with jitter
  • 429 handling (respect Retry-After header; otherwise back off)

Indicator:

  • Last successful refresh time
  • Next scheduled refresh
  • Current backoff state (if any)

7. Keyboard Shortcuts (v1.0)

Description: Keyboard-first operation for reduced friction.

Key Action
j / k Navigate down / up
Enter Open selected item in GitLab
h Mark handled
s Snooze (opens snooze picker)
d Dismiss (mark as done in GitLab)
w Add to / remove from watchlist
/ Focus search/filter

8. Focus Mode (Optional)

Description: Show only the next N items (default 3) to reduce decision load.

Purpose:

  • Convert "overwhelm" into "sequence"
  • Reduce choices, increase throughput
  • ADHD-optimized: work the queue, don't manage the list

Behavior:

  • Primary action emphasized ("Open", then "Handled"/"Snooze")
  • Toggle: Focus / All Items
  • Focus queue is top N unhandled items by creation date

Technical Architecture

Tech Stack

Component Technology
Framework TanStack Start
Styling Tailwind CSS
Runtime Node.js (local)
State Persistence JSON file with atomic writes (local)
Secret Storage OS keychain (preferred) or encrypted local store
GitLab Integration REST API with Personal Access Token

Deployment

  • Local only: Runs on localhost
  • No external hosting: No cloud deployment, no auth flows
  • Single user: No multi-tenancy

GitLab API

Authentication: Personal Access Token (PAT) with read_api scope

Primary Endpoints:

GET /api/v4/todos?state=pending&per_page=100

POST /api/v4/todos/:id/mark_as_done

Response Structure (relevant fields):

interface GitLabTodo {
  id: number;
  action_name:
    | 'assigned'
    | 'mentioned'
    | 'build_failed'
    | 'marked'
    | 'approval_required'
    | 'unmergeable'
    | 'directly_addressed'
    | 'merge_train_removed'
    | 'member_access_requested'
    | string; // forward-compatible for new action types
  target_type: 'MergeRequest' | 'Issue' | 'Commit' | 'Epic' | 'DesignManagement::Design' | string;
  target: {
    id: number;
    iid: number;
    title: string;
    web_url?: string; // optional; may not be present for all target types
  };
  target_url: string; // canonical "Open" URL - use this for navigation
  author: {
    id: number;
    name: string;
    avatar_url: string;
  };
  project: {
    id: number;
    name: string;
    path_with_namespace: string;
  };
  created_at: string;
}

Local State

interface LocalState {
  schemaVersion: number; // for migrations

  handledByDate: {
    [localDate: string]: { // YYYY-MM-DD in local time
      [todoId: number]: {
        handledAt: string;  // ISO timestamp
        todo: GitLabTodo;   // Snapshot for Done Today display
      }
    }
  };

  snoozedTodos: {
    [todoId: number]: {
      wakeAt: string;     // ISO timestamp
      snoozedAt: string;  // ISO timestamp
      todo: GitLabTodo;   // snapshot
    }
  };

  watchlist: {
    [watchKey: string]: { // e.g., "MergeRequest:123" or "Issue:456"
      targetType: string;
      projectId?: number;
      targetId: number;
      targetIid?: number;
      targetUrl: string;
      lastSeenUpdatedAt?: string; // ISO - when we last observed the target
      lastCheckedAt?: string;     // ISO - when we last polled
      addedAt: string;            // ISO
      muted?: boolean;
    }
  };
}

Storage: ~/.config/gitlab-inbox/state.json

Persistence Strategy:

  • Atomic writes: write to state.json.tmp, then rename to state.json
  • Keep state.json.bak as last-known-good before each write
  • Validate JSON schema on load; if invalid, fall back to backup and surface warning
  • Schema version for forward migrations

User Interface

Layout

+--------------------------------------------------+
|  GitLab Inbox    [Focus] [Refresh]   🟢 2m ago   |
|  [Inbox] [Snoozed] [Watchlist] [Done Today]      |
+--------------------------------------------------+
|                                                  |
|  > [mentioned] Fix login bug                     |
|    Alice Smith · infra-frontend · 2h ago         |
|                    [Snooze] [Handle] [Open]      |
|                                                  |
|    [review_requested] Add caching layer          |
|    Bob Jones · api-service · 1d ago              |
|                    [Snooze] [Handle] [Open]      |
|                                                  |
|    [assigned] Update documentation               |
|    Carol White · docs · 3d ago                   |
|                    [Snooze] [Handle] [Open]      |
|                                                  |
+--------------------------------------------------+
|  j/k: navigate  Enter: open  h: handle  s: snooze |
+--------------------------------------------------+

Action Badge Colors

Action Color Meaning
mentioned Blue Someone mentioned you
assigned Purple Assigned to you
approval_required Yellow Needs your approval
build_failed Red Pipeline failure
directly_addressed Cyan Direct @ mention
unmergeable Orange MR has conflicts
marked Gray Marked as todo
merge_train_removed Red Removed from merge train
member_access_requested Teal Access request
(unknown) Gray Forward-compatible fallback

States

  • Loading: Skeleton cards while fetching
  • Empty: "All clear! No pending items." message
  • Error: Connection error with retry button
  • Stale: Visual indicator if data is old (> 5 min since last successful refresh)
  • Backoff: Indicator showing retry status when experiencing errors

User Flows

Flow 1: Morning Check-in

  1. Open GitLab Inbox (already running in background tab)
  2. See list of todos sorted by newest first
  3. Press Enter to open in GitLab
  4. Handle the item (reply, review, etc.)
  5. Return to Inbox, press h to mark handled
  6. Item moves to Done Today
  7. Repeat until Inbox is empty

Flow 2: Triage with Snooze

  1. See inbox with 12 items
  2. Quickly triage: handle 3, snooze 5 until tomorrow, dismiss 2 already-resolved
  3. Inbox now shows 2 items to focus on
  4. Tomorrow: snoozed items wake up and return to inbox

Flow 3: Awaiting Reply

  1. Handle a todo (you replied to someone's question)
  2. Toggle "Add to Watchlist" when marking handled
  3. Item appears in Watchlist view
  4. Later: see "New activity" indicator when they respond
  5. Open, read response, remove from watchlist

Flow 4: Focus Session

  1. Enable Focus Mode
  2. See only top 3 items
  3. Work through them sequentially
  4. As items complete, next ones appear
  5. Reduced decision fatigue

Flow 5: End-of-Day Review

  1. Navigate to Done Today view
  2. See all items handled today
  3. Satisfaction from visible progress

Success Metrics

Metric Target Measurement
Time to awareness < 2 min Time from GitLab event to user seeing it
Daily items handled Increased Compare to baseline (manual tracking)
Context switches Reduced Fewer GitLab tabs open simultaneously
Snooze usage Regular Items snoozed vs dismissed (healthy ratio = snooze used)
Reply awareness High Watchlist items caught before manual check
User satisfaction Qualitative Does this reduce ADHD-related friction?

Risks and Mitigations

Risk Impact Mitigation
GitLab API rate limits Polling blocked Configurable interval, backoff + jitter, respect 429/Retry-After
Token expiration/rotation App stops working Clear error state + setup flow; surface expiry guidance and re-auth path
State file corruption Lose handled/snoozed/watch state Atomic writes (tmp+rename), schema validation on load, keep last-known-good backup
GitLab API changes App breaks Pin to known API version, monitor deprecations, forward-compatible types
Token leakage Security incident Store in OS keychain, not in repo-adjacent files

Future Considerations (Post v1.0)

  • Grouping: By project, by action type
  • Stale highlighting: Visual alert for items waiting > X days
  • Desktop notifications: OS-level alerts for new high-priority items
  • Quick actions: Approve MR, close issue directly from app
  • Multiple GitLab instances: Connect to both gitlab.com and self-hosted
  • Done history: View handled items from yesterday, this week

Implementation Phases

Phase 0: Setup & Auth

  • First-run setup wizard (GitLab URL + token)
  • Token storage implementation (keychain/encrypted local)
  • Connectivity check (/todos, auth failure UX)
  • Clear error states for invalid/expired tokens

Phase 1: Foundation

  • Initialize TanStack Start project
  • Set up Tailwind CSS
  • Create GitLab API client with PAT auth
  • Fetch and display todos in basic list (using target_url for navigation)
  • Implement click-to-open

Phase 2: Core Workflow

  • Add local storage with atomic writes + backup
  • Implement date-bucketed handled state
  • Implement "Mark Handled" action
  • Create Done Today view
  • Add keyboard shortcuts (minimal set: j/k/Enter/h/s/d)
  • Add Snooze + Snoozed view
  • Filter handled/snoozed todos from Inbox

Phase 3: Reliability & Awareness

  • Background polling with configurable interval
  • Backoff/jitter + 429 handling
  • Last successful refresh tracking
  • Watchlist ("Awaiting Reply") implementation
  • Per-target polling for watched items (small set)
  • Add manual refresh button
  • Relative time display
  • Action type badges with colors
  • Loading and error states
  • Connection status indicator

Phase 4: Polish

  • Focus Mode implementation
  • Snooze time picker refinement
  • Keyboard shortcut help overlay
  • State migration handling (schemaVersion)
  • Edge case handling (DST, timezone changes)

Appendix

Environment Configuration

Primary configuration:

  • URL + settings in: ~/.config/gitlab-inbox/config.json
  • Token stored in OS keychain (preferred)

Optional (dev-only) .env.local support:

GITLAB_URL=https://gitlab.yourcompany.com
GITLAB_TOKEN=glpat-xxxxxxxxxxxx

Config file structure:

{
  "gitlabUrl": "https://gitlab.yourcompany.com",
  "pollingInterval": 60,
  "focusModeCount": 3
}

Creating a GitLab PAT

  1. Go to GitLab → User Settings → Access Tokens
  2. Create token with read_api scope
  3. Set expiration (note: tokens expire at midnight UTC on expiry date)
  4. Save token via setup wizard (stored in keychain)
  5. Token never leaves local machine

Project Structure

gitlab-inbox/
├── app/
│   ├── routes/
│   │   ├── __root.tsx
│   │   ├── index.tsx          # Inbox view
│   │   ├── snoozed.tsx        # Snoozed view
│   │   ├── watchlist.tsx      # Watchlist view
│   │   ├── done.tsx           # Done Today view
│   │   └── setup.tsx          # First-run setup
│   ├── components/
│   │   ├── TodoCard.tsx
│   │   ├── TodoList.tsx
│   │   ├── ActionBadge.tsx
│   │   ├── Header.tsx
│   │   ├── SnoozePicker.tsx
│   │   ├── FocusMode.tsx
│   │   └── KeyboardHelp.tsx
│   ├── lib/
│   │   ├── gitlab.ts          # API client
│   │   ├── storage.ts         # Atomic state persistence
│   │   ├── keychain.ts        # Token storage
│   │   ├── polling.ts         # Polling state machine
│   │   ├── snooze.ts          # Snooze logic + wake checking
│   │   ├── watchlist.ts       # Watchlist polling
│   │   └── types.ts
│   └── app.tsx
├── package.json
├── tailwind.config.ts
└── vite.config.ts

Test Strategy

Unit Tests:

  • State normalization and migration
  • Snooze wake time calculations
  • Date bucketing logic (timezone handling)
  • Polling backoff calculations

Integration Tests (mocked GitLab API):

  • /todos response parsing
  • mark_as_done endpoint calls
  • Error handling (401, 429, network errors)
  • State persistence round-trip (write + read)
  • Backup recovery on corruption

Manual Testing:

  • First-run setup flow
  • Keyboard navigation
  • Snooze + wake cycle
  • Watchlist activity detection