diff --git a/plans/gitlab-todos-notifications-integration.md b/plans/gitlab-todos-notifications-integration.md new file mode 100644 index 0000000..e873e3c --- /dev/null +++ b/plans/gitlab-todos-notifications-integration.md @@ -0,0 +1,278 @@ +--- +plan: true +title: "GitLab TODOs Integration" +status: proposed +iteration: 1 +target_iterations: 3 +beads_revision: 1 +related_plans: [] +created: 2026-02-23 +updated: 2026-02-23 +--- + +# GitLab TODOs Integration + +## Summary + +Add GitLab TODO support to lore. Todos are fetched during sync, stored locally, and surfaced through: +1. A new `--todos` section in `lore me` +2. Enrichment of the activity feed in `lore me` +3. A standalone `lore todos` command + +**Scope:** Read-only. No mark-as-done operations. + +--- + +## Design Decisions (from interview) + +| Decision | Choice | +|----------|--------| +| Write operations | **Read-only** — no mark-as-done | +| Storage | **Persist locally** in SQLite | +| Integration | Three-way: activity enrichment + `--todos` flag + `lore todos` | +| Action types | Core only: assigned, mentioned, directly_addressed, approval_required, build_failed, unmergeable | +| Niche actions | Skip display (but store): merge_train_removed, member_access_requested, marked | +| Project filter | **Always account-wide** — `--project` does NOT filter todos | +| Sync timing | During normal `lore sync` | +| Non-synced projects | Include with `[external]` indicator | +| Attention state | **Separate signal** — todos don't boost attention | +| Summary header | Include pending todo count | +| Grouping | By action type: Assignments \| Mentions \| Approvals \| Build Issues | +| History | **Pending only** — done todos not tracked | +| `lore todos` filters | **None** — show all pending, simple | +| Robot mode | Yes, standard envelope | +| Target types | All GitLab supports (Issue, MR, Epic, Commit, etc.) | + +--- + +## Out of Scope + +- Write operations (mark as done) +- Done todo history tracking +- Filters on `lore todos` command +- Todo-based attention state boosting +- Notification settings API integration (deferred to separate plan) + +--- + +## Acceptance Criteria + +### AC-1: Database Schema + +- [ ] **AC-1.1:** Create `todos` table with columns: + - `id` INTEGER PRIMARY KEY + - `gitlab_todo_id` INTEGER NOT NULL UNIQUE + - `project_id` INTEGER REFERENCES projects(id) ON DELETE SET NULL (nullable for non-synced) + - `target_type` TEXT NOT NULL (Issue, MergeRequest, Commit, Epic, etc.) + - `target_id` INTEGER (GitLab ID of target entity) + - `target_iid` INTEGER (IID for issues/MRs, nullable) + - `target_url` TEXT NOT NULL + - `target_title` TEXT + - `action_name` TEXT NOT NULL (assigned, mentioned, etc.) + - `author_id` INTEGER + - `author_username` TEXT + - `body` TEXT (the todo message/snippet) + - `state` TEXT NOT NULL (pending) + - `created_at` INTEGER NOT NULL (epoch ms) + - `updated_at` INTEGER NOT NULL (epoch ms) + - `synced_at` INTEGER NOT NULL (epoch ms) + - `project_path` TEXT (for display even if project not synced) +- [ ] **AC-1.2:** Create index `idx_todos_state_action` on `(state, action_name)` +- [ ] **AC-1.3:** Create index `idx_todos_target` on `(target_type, target_id)` +- [ ] **AC-1.4:** Create index `idx_todos_created` on `(created_at DESC)` +- [ ] **AC-1.5:** Migration increments schema version + +### AC-2: GitLab API Client + +- [ ] **AC-2.1:** Add `fetch_todos()` method to GitLab client +- [ ] **AC-2.2:** Fetch only `state=pending` todos +- [ ] **AC-2.3:** Handle pagination (use existing pagination pattern) +- [ ] **AC-2.4:** Parse all target types GitLab returns +- [ ] **AC-2.5:** Extract project path from `target_url` for non-synced projects + +### AC-3: Sync Pipeline + +- [ ] **AC-3.1:** Add todos sync step to `lore sync` pipeline +- [ ] **AC-3.2:** Sync todos AFTER issues/MRs (ordering consistency) +- [ ] **AC-3.3:** Snapshot semantics: fetch all pending, upsert, delete missing (= marked done elsewhere) +- [ ] **AC-3.4:** Track `synced_at` timestamp +- [ ] **AC-3.5:** Log todo sync stats: fetched, inserted, updated, deleted +- [ ] **AC-3.6:** Add `--no-todos` flag to skip todo sync + +### AC-4: Action Type Handling + +- [ ] **AC-4.1:** Store ALL action types from GitLab +- [ ] **AC-4.2:** Display only core actions: + - `assigned` — assigned to issue/MR + - `mentioned` — @mentioned in comment + - `directly_addressed` — @mentioned at start of comment + - `approval_required` — approval needed on MR + - `build_failed` — CI failed on your MR + - `unmergeable` — merge conflicts on your MR +- [ ] **AC-4.3:** Skip display (but store) niche actions: `merge_train_removed`, `member_access_requested`, `marked` + +### AC-5: `lore todos` Command + +- [ ] **AC-5.1:** New subcommand `lore todos` (alias: `todo`) +- [ ] **AC-5.2:** Display all pending todos, no filters +- [ ] **AC-5.3:** Group by action type: Assignments | Mentions | Approvals | Build Issues +- [ ] **AC-5.4:** Per-todo display: target title, project path, author, age, action +- [ ] **AC-5.5:** Flag non-synced project todos with `[external]` indicator +- [ ] **AC-5.6:** Human-readable output with colors/icons +- [ ] **AC-5.7:** Robot mode: standard `{ok, data, meta}` envelope + +### AC-6: `lore me --todos` Section + +- [ ] **AC-6.1:** Add `--todos` flag to `MeArgs` +- [ ] **AC-6.2:** When no section flags: show todos in full dashboard +- [ ] **AC-6.3:** When `--todos` flag only: show only todos section +- [ ] **AC-6.4:** Todos section grouped by action type +- [ ] **AC-6.5:** Todos NOT filtered by `--project` (always account-wide) +- [ ] **AC-6.6:** Robot mode includes `todos` array in dashboard response + +### AC-7: `lore me` Summary Header + +- [ ] **AC-7.1:** Add `pending_todo_count` to `MeSummary` struct +- [ ] **AC-7.2:** Display todo count in summary line (human mode) +- [ ] **AC-7.3:** Include `pending_todo_count` in robot mode summary + +### AC-8: Activity Feed Enrichment + +- [ ] **AC-8.1:** Todos with local issue/MR target appear in activity feed +- [ ] **AC-8.2:** New `ActivityEventType::Todo` variant +- [ ] **AC-8.3:** Todo events show: action type, author, target in summary +- [ ] **AC-8.4:** Sorted chronologically with other activity events +- [ ] **AC-8.5:** Respect `--since` filter on todo `created_at` + +### AC-9: Non-Synced Project Handling + +- [ ] **AC-9.1:** Store todos even if target project not in config +- [ ] **AC-9.2:** Display `[external]` indicator for non-synced project todos +- [ ] **AC-9.3:** Show project path (extracted from target URL) +- [ ] **AC-9.4:** Graceful fallback when target title unavailable + +### AC-10: Attention State + +- [ ] **AC-10.1:** Attention state calculation remains note-based (unchanged) +- [ ] **AC-10.2:** Todos are separate signal, do not affect attention state +- [ ] **AC-10.3:** Document this design decision in code comments + +### AC-11: Robot Mode Schema + +- [ ] **AC-11.1:** `lore todos --robot` returns: + ```json + { + "ok": true, + "data": { + "todos": [{ + "id": 123, + "gitlab_todo_id": 456, + "action": "mentioned", + "target_type": "Issue", + "target_iid": 42, + "target_title": "Fix login bug", + "target_url": "https://...", + "project_path": "group/repo", + "author_username": "jdoe", + "body": "Hey @you, can you look at this?", + "created_at_iso": "2026-02-20T10:00:00Z", + "is_external": false + }], + "counts": { + "total": 8, + "assigned": 2, + "mentioned": 5, + "approval_required": 1, + "build_failed": 0, + "unmergeable": 0 + } + }, + "meta": {"elapsed_ms": 42} + } + ``` +- [ ] **AC-11.2:** `lore me --robot` includes `todos` and `pending_todo_count` in response +- [ ] **AC-11.3:** Support `--fields minimal` for token efficiency + +### AC-12: Documentation + +- [ ] **AC-12.1:** Update CLAUDE.md with `lore todos` command reference +- [ ] **AC-12.2:** Update `lore robot-docs` manifest with todos schema +- [ ] **AC-12.3:** Add todos to CLI help output + +### AC-13: Quality Gates + +- [ ] **AC-13.1:** `cargo check --all-targets` passes +- [ ] **AC-13.2:** `cargo clippy --all-targets -- -D warnings` passes +- [ ] **AC-13.3:** `cargo fmt --check` passes +- [ ] **AC-13.4:** `cargo test` passes with new tests + +--- + +## Technical Notes + +### GitLab API Endpoint + +``` +GET /api/v4/todos?state=pending +``` + +Response fields: id, project, author, action_name, target_type, target, target_url, body, state, created_at, updated_at + +### Sync Deletion Strategy + +Snapshot semantics: a todo disappearing from API response means it was marked done elsewhere. Delete from local DB to stay in sync. + +### Project Path Extraction + +For non-synced projects, extract path from `target_url`: +``` +https://gitlab.com/group/subgroup/repo/-/issues/42 + ^^^^^^^^^^^^^^^^^ extract this +``` + +### Action Type Grouping + +| Group | Actions | +|-------|---------| +| Assignments | `assigned` | +| Mentions | `mentioned`, `directly_addressed` | +| Approvals | `approval_required` | +| Build Issues | `build_failed`, `unmergeable` | + +--- + +## Rollout Slices + +### Slice A: Schema + Client +- Migration 028 +- `GitLabTodo` type +- `fetch_todos()` client method +- Unit tests for deserialization + +### Slice B: Sync Integration +- `src/ingestion/todos.rs` +- Integrate into `lore sync` +- `--no-todos` flag +- Sync stats + +### Slice C: `lore todos` Command +- CLI args + dispatch +- Human + robot rendering +- Autocorrect aliases + +### Slice D: `lore me` Integration +- `--todos` flag +- Summary count +- Activity feed enrichment + +### Slice E: Polish +- Edge case tests +- Documentation updates +- `robot-docs` manifest + +--- + +## References + +- [GitLab To-Do List API](https://docs.gitlab.com/api/todos/) +- [GitLab User Todos](https://docs.gitlab.com/user/todos/)