From 9b836b0405c071cf2d10ff397229524f940fc3c9 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 10:59:34 +1000 Subject: [PATCH 01/17] chore(porch): 809 init pir --- .../status.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml new file mode 100644 index 000000000..3ba25c8c7 --- /dev/null +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -0,0 +1,18 @@ +id: '809' +title: vscode-backlog-view-toggle-bet +protocol: pir +phase: plan +plan_phases: [] +current_plan_phase: null +gates: + plan-approval: + status: pending + dev-approval: + status: pending + pr: + status: pending +iteration: 1 +build_complete: false +history: [] +started_at: '2026-05-28T00:59:34.520Z' +updated_at: '2026-05-28T00:59:34.521Z' From f4b10b3c9160d0b6fcb16251aeb072dff76aae5c Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 11:02:03 +1000 Subject: [PATCH 02/17] [PIR #809] Plan draft --- .../809-vscode-backlog-view-toggle-bet.md | 61 +++++++++++++++++++ codev/state/pir-809_thread.md | 9 +++ 2 files changed, 70 insertions(+) create mode 100644 codev/plans/809-vscode-backlog-view-toggle-bet.md create mode 100644 codev/state/pir-809_thread.md diff --git a/codev/plans/809-vscode-backlog-view-toggle-bet.md b/codev/plans/809-vscode-backlog-view-toggle-bet.md new file mode 100644 index 000000000..6383eb4f7 --- /dev/null +++ b/codev/plans/809-vscode-backlog-view-toggle-bet.md @@ -0,0 +1,61 @@ +# PIR Plan: Backlog View Mine/All Toggle + +## Understanding + +The Backlog view in the VS Code extension currently renders every open GitHub issue (`packages/vscode/src/views/backlog.ts:62-85`). Items assigned to the current user (auto-detected via `OverviewData.currentUser`) are sorted to the top of each `area/*` group and shown with the `account` icon (`backlog.ts:98-116, 123-132`); everything else shows the `issues` icon. The user has to scan the full list to find their own work. + +The issue asks for a title-bar toggle that flips between "mine only" (default) and "show all", following the existing two-commands-one-config-flag pattern used for `codev.buildersAutoCollapse` and `codev.buildersFileViewAsTree` (`extension.ts:601-608`, `package.json:92-111, 368-387, 556-565`). + +## Proposed Change + +Add a `codev.backlogShowAll` boolean config (default `false`) and two paired commands (`codev.showBacklogAll`, `codev.showBacklogMineOnly`) registered as `view/title` actions on `codev.backlog`. The config is mirrored to a `setContext` key so the menu `when` clauses can swap the visible icon. `BacklogProvider` reads the config and applies a filter predicate inside its existing root/group rendering paths. When `currentUser` is unavailable, the filter falls back to showing all items (avoids an empty view when `gh` isn't authenticated). When mine-only mode filters everything out, the root renders a single non-clickable placeholder row. + +Why this approach over alternatives: it reuses the convention already established for the two other view-title toggles in the extension verbatim — same config-flag-plus-paired-commands shape, same `setContext`-key wiring, same `onDidChangeConfiguration` listener that calls `provider.refresh()`. That keeps the cognitive cost of "another toggle" near zero and matches the issue body's explicit reference to the existing pattern. + +## Files to Change + +- `packages/vscode/package.json` + - `contributes.commands`: add `codev.showBacklogAll` (title `Codev: Show All Backlog Items`, icon `$(eye)`) and `codev.showBacklogMineOnly` (title `Codev: Show Only My Backlog Items`, icon `$(eye-closed)`). Icon choice: `eye`/`eye-closed` reads as a visibility filter and is distinct from the `account`/`issues` per-row icons. + - `contributes.menus["view/title"]`: add two entries for `codev.backlog` with the `when` clauses from the issue (`view == codev.backlog && !codev.backlogShowAll` for the `showBacklogAll` button, `view == codev.backlog && codev.backlogShowAll` for `showBacklogMineOnly`). Group `navigation`. Place adjacent to the existing `codev.refreshOverview` entry for `codev.backlog` (`package.json:393-397`). + - `contributes.configuration.properties`: add `codev.backlogShowAll` (boolean, default `false`, description matching the other toggles' tone). + +- `packages/vscode/src/extension.ts` + - Register the two commands alongside the existing toggle commands (`extension.ts:601-608`). Each updates `codev.backlogShowAll` via `vscode.ConfigurationTarget.Global`. + - Add a `readBacklogShowAll()` helper and the matching `setContext` seeding + `onDidChangeConfiguration` listener, mirroring the file-view-as-tree block (`extension.ts:326-334`). On config change: update the context key AND call `backlogProvider.refresh()`. Lift the `backlogProvider` binding out of the `{ ... }` block (it's currently a `const` declared at `extension.ts:250` — already in scope at the listener site). + +- `packages/vscode/src/views/backlog.ts` + - Add `refresh(): void { this.changeEmitter.fire(); }`, matching `BuildersProvider.refresh()` (`views/builders.ts:74-76`). + - Add a private `filterToMine(items, data)` helper that returns the filtered list when `backlogShowAll === false` AND `currentUser` is present; otherwise returns the input unchanged. Call it once inside `orderedSpawnable` (which is the single chokepoint feeding both `rootChildren` and `rowsForGroup`). + - Add an empty-state branch in `rootChildren()`: if the post-filter `items` array is empty AND the config is in mine-only mode AND `currentUser` was present (i.e. the user is genuinely seeing zero items, not just a pre-data render), return a single `vscode.TreeItem` whose label is `(no backlog items assigned to you — click the eye icon to see all)` and whose `command` is undefined (non-clickable). The existing "no data" path (`!data` → return `[]`) stays untouched so a not-yet-loaded view still renders nothing rather than the placeholder. + +- `packages/vscode/tests/unit/views/backlog.test.ts` (existing file — read first; if no existing tests cover `rootChildren`/`orderedSpawnable`, add a small suite) + - Test: mine-only mode filters out non-assigned items. + - Test: mine-only mode with no `currentUser` (gh unavailable) returns all items. + - Test: show-all mode returns all items regardless of `currentUser`. + - Test: mine-only mode with zero matches renders the placeholder row. + - Test: existing icon-swap behavior on assigned items is preserved in show-all mode. + +## Risks & Alternatives Considered + +- **Risk: config change doesn't re-render the tree.** Mitigation: explicit `backlogProvider.refresh()` in the `onDidChangeConfiguration` listener (same fix pattern used at `extension.ts:333`). +- **Risk: empty state when `currentUser` is briefly unset on first load** (e.g. before overview data arrives). Mitigation: the empty-state placeholder only renders when `currentUser` is present and the filter yields zero — otherwise the view falls back to showing all items. Pre-data state (`!data`) returns `[]` unchanged. +- **Risk: the `account` icon becomes redundant in mine-only mode** (every visible row is "yours"). Decision: keep the icon-swap logic unchanged per the acceptance criterion "no regression to the existing icon-swap behavior". In mine-only mode every row shows `account`, which is informative-but-redundant — acceptable, and changing it would break the show-all behavior the criterion guards. +- **Alternative: single command that flips state, with a stateful icon.** Rejected — VS Code menu `when` clauses can't easily express "show this icon variant when X" within a single command, so the two-commands pattern is what the rest of the extension uses. +- **Alternative: store toggle state in `workspaceState` instead of config.** Rejected — config is per-user across windows (matches the issue's "persists across VS Code restarts" criterion globally, not per-workspace) and matches the precedent set by the two existing toggles. +- **Alternative: filter inside `spawnableBacklog`.** Rejected — that helper is also consumed by tests / potentially other call sites; mine-only filtering is a view-layer concern, so it belongs inside `BacklogProvider`. + +## Test Plan + +**Unit (Vitest, packages/vscode/tests/unit/views/backlog.test.ts):** +- Cover the four filter cases enumerated under Files to Change. + +**Manual (at the `dev-approval` gate, via `afx dev pir-809`):** +1. Default state on first install: open the Backlog view. Title bar shows the `$(eye)` "show all" icon. Tree shows only issues assigned to me. If the assigned set is empty, the placeholder row is visible. +2. Click the `$(eye)` icon → list expands to all open issues, icon flips to `$(eye-closed)`. Assigned items still show the `account` icon, others show `issues`. +3. Click `$(eye-closed)` → list filters back to mine-only. +4. Restart VS Code (Developer: Reload Window). Toggle state persists. +5. Sign out of `gh` (or temporarily break auth) and reload the overview. With `currentUser` null, the view renders all items regardless of toggle state — no empty-view trap. +6. With assigned items present and the toggle in mine-only mode, the existing icon swap and the "assigned to you" description still appear on the rows that are mine (no regression). +7. The right-click context menu and click-to-view behaviour on rows are unchanged. + +**Build/lint:** `pnpm --filter @cluesmith/codev-vscode-extension build` and `pnpm --filter @cluesmith/codev-vscode-extension lint` clean. diff --git a/codev/state/pir-809_thread.md b/codev/state/pir-809_thread.md new file mode 100644 index 000000000..c42915ae9 --- /dev/null +++ b/codev/state/pir-809_thread.md @@ -0,0 +1,9 @@ +# PIR #809 Thread — vscode backlog mine/all toggle + +## Plan phase +- Issue: add a Mine/All toggle to the Backlog view title bar with `codev.backlogShowAll` config flag (default false = mine-only). +- Pattern: two-commands + one config flag + paired `when` clauses, mirroring `codev.buildersAutoCollapse` / `codev.buildersFileViewAsTree`. +- Plan committed to `codev/plans/809-vscode-backlog-view-toggle-bet.md`. +- Filter lives in `BacklogProvider.orderedSpawnable` (the single chokepoint feeding both root and per-group rendering). +- Empty-state placeholder renders only when `currentUser` is known AND filter yields zero — avoids confusing the not-yet-loaded path. +- Awaiting `plan-approval`. From 1f64ca164b2528d0b950e1b15d1bbd2a64bc65b9 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 11:02:09 +1000 Subject: [PATCH 03/17] chore(porch): 809 plan-approval gate-requested --- codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index 3ba25c8c7..20b92c02c 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -7,6 +7,7 @@ current_plan_phase: null gates: plan-approval: status: pending + requested_at: '2026-05-28T01:02:09.299Z' dev-approval: status: pending pr: @@ -15,4 +16,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T00:59:34.521Z' +updated_at: '2026-05-28T01:02:09.300Z' From 0fc3d867ddaecb120e6f96e1ff08686df1147fee Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 14:40:54 +1000 Subject: [PATCH 04/17] chore(porch): 809 plan-approval gate-approved --- .../projects/809-vscode-backlog-view-toggle-bet/status.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index 20b92c02c..fed4f0cb8 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -6,8 +6,9 @@ plan_phases: [] current_plan_phase: null gates: plan-approval: - status: pending + status: approved requested_at: '2026-05-28T01:02:09.299Z' + approved_at: '2026-05-28T04:40:54.922Z' dev-approval: status: pending pr: @@ -16,4 +17,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T01:02:09.300Z' +updated_at: '2026-05-28T04:40:54.922Z' From 46def5167fec2264aea89900f80e33d9f6117433 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 14:43:11 +1000 Subject: [PATCH 05/17] [PIR #809] Plan: correct test file path and unit-test scope --- codev/plans/809-vscode-backlog-view-toggle-bet.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/codev/plans/809-vscode-backlog-view-toggle-bet.md b/codev/plans/809-vscode-backlog-view-toggle-bet.md index 6383eb4f7..7c72a47d6 100644 --- a/codev/plans/809-vscode-backlog-view-toggle-bet.md +++ b/codev/plans/809-vscode-backlog-view-toggle-bet.md @@ -25,15 +25,16 @@ Why this approach over alternatives: it reuses the convention already establishe - `packages/vscode/src/views/backlog.ts` - Add `refresh(): void { this.changeEmitter.fire(); }`, matching `BuildersProvider.refresh()` (`views/builders.ts:74-76`). - - Add a private `filterToMine(items, data)` helper that returns the filtered list when `backlogShowAll === false` AND `currentUser` is present; otherwise returns the input unchanged. Call it once inside `orderedSpawnable` (which is the single chokepoint feeding both `rootChildren` and `rowsForGroup`). + - Export a pure helper `filterMine(items: OverviewBacklogItem[], currentUser: string | null | undefined): OverviewBacklogItem[]` that returns the assignment-filtered list when `currentUser` is non-empty, otherwise returns the input unchanged. Pure-function shape mirrors `spawnableBacklog` (`backlog.ts:15-17`) so it can be unit-tested without mocking `vscode`. + - Inside `BacklogProvider`, read `codev.backlogShowAll` (default `false`) at the start of each render via `vscode.workspace.getConfiguration('codev')` and apply `filterMine` inside `orderedSpawnable` when the toggle is off. `orderedSpawnable` is the single chokepoint feeding both `rootChildren` and `rowsForGroup`. - Add an empty-state branch in `rootChildren()`: if the post-filter `items` array is empty AND the config is in mine-only mode AND `currentUser` was present (i.e. the user is genuinely seeing zero items, not just a pre-data render), return a single `vscode.TreeItem` whose label is `(no backlog items assigned to you — click the eye icon to see all)` and whose `command` is undefined (non-clickable). The existing "no data" path (`!data` → return `[]`) stays untouched so a not-yet-loaded view still renders nothing rather than the placeholder. -- `packages/vscode/tests/unit/views/backlog.test.ts` (existing file — read first; if no existing tests cover `rootChildren`/`orderedSpawnable`, add a small suite) +- `packages/vscode/src/test/backlog.test.ts` (extend the existing file alongside the `spawnableBacklog` suite) - Test: mine-only mode filters out non-assigned items. - Test: mine-only mode with no `currentUser` (gh unavailable) returns all items. - - Test: show-all mode returns all items regardless of `currentUser`. - - Test: mine-only mode with zero matches renders the placeholder row. - - Test: existing icon-swap behavior on assigned items is preserved in show-all mode. + - Test: `currentUser` matching is case-insensitive (`"Alice"` matches `"alice"`). + - Test: empty input returns empty. + - The full `BacklogProvider.rootChildren()` rendering (icons, placeholder row, config read) is not unit-tested — those paths touch `vscode.TreeItem` / `vscode.workspace.getConfiguration` and are validated at the `dev-approval` gate via the manual checklist. The pure-function tests cover the filter logic that determines correctness; the wrapper is glue. ## Risks & Alternatives Considered @@ -46,8 +47,8 @@ Why this approach over alternatives: it reuses the convention already establishe ## Test Plan -**Unit (Vitest, packages/vscode/tests/unit/views/backlog.test.ts):** -- Cover the four filter cases enumerated under Files to Change. +**Unit (Mocha + `assert`, extends `packages/vscode/src/test/backlog.test.ts`):** +- Cover the four `filterMine` cases enumerated under Files to Change. **Manual (at the `dev-approval` gate, via `afx dev pir-809`):** 1. Default state on first install: open the Backlog view. Title bar shows the `$(eye)` "show all" icon. Tree shows only issues assigned to me. If the assigned set is empty, the placeholder row is visible. From 84d14b750ee5346d1683c96552a2a3e2da0e3dcd Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 14:43:18 +1000 Subject: [PATCH 06/17] chore(porch): 809 implement phase-transition --- codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index fed4f0cb8..c042999bd 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -1,7 +1,7 @@ id: '809' title: vscode-backlog-view-toggle-bet protocol: pir -phase: plan +phase: implement plan_phases: [] current_plan_phase: null gates: @@ -17,4 +17,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T04:40:54.922Z' +updated_at: '2026-05-28T04:43:18.375Z' From be66279066d165e91fbd83cd8f216c5749740e67 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:01:14 +1000 Subject: [PATCH 07/17] [PIR #809] vscode: backlog mine/all toggle - new pure helper filterMine in views/backlog-filter.ts, unit-tested via __tests__/backlog-filter.test.ts - BacklogProvider.orderedSpawnable applies filterMine when codev.backlogShowAll is off; placeholder row when mine-only filters to zero with currentUser known - two paired commands codev.showBacklogAll / codev.showBacklogMineOnly registered as view/title actions on codev.backlog, gated by the codev.backlogShowAll context key - onDidChangeConfiguration listener mirrors the setting into the context key and refreshes the provider, matching the existing buildersAutoCollapse / buildersFileViewAsTree pattern --- packages/vscode/package.json | 25 +++++++++ .../src/__tests__/backlog-filter.test.ts | 53 +++++++++++++++++++ packages/vscode/src/extension.ts | 20 +++++++ packages/vscode/src/views/backlog-filter.ts | 21 ++++++++ packages/vscode/src/views/backlog.ts | 37 ++++++++++++- 5 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 packages/vscode/src/__tests__/backlog-filter.test.ts create mode 100644 packages/vscode/src/views/backlog-filter.ts diff --git a/packages/vscode/package.json b/packages/vscode/package.json index aa3f83903..6bb8db8fb 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -109,6 +109,16 @@ "title": "Codev: View Changed Files as List", "icon": "$(list-flat)" }, + { + "command": "codev.showBacklogAll", + "title": "Codev: Show All Backlog Items", + "icon": "$(eye)" + }, + { + "command": "codev.showBacklogMineOnly", + "title": "Codev: Show Only My Backlog Items", + "icon": "$(eye-closed)" + }, { "command": "codev.refreshTeam", "title": "Codev: Refresh Team", @@ -425,6 +435,16 @@ "when": "view == codev.backlog", "group": "navigation" }, + { + "command": "codev.showBacklogAll", + "when": "view == codev.backlog && !codev.backlogShowAll", + "group": "navigation" + }, + { + "command": "codev.showBacklogMineOnly", + "when": "view == codev.backlog && codev.backlogShowAll", + "group": "navigation" + }, { "command": "codev.refreshOverview", "when": "view == codev.recentlyClosed", @@ -592,6 +612,11 @@ "type": "boolean", "default": true, "description": "Render a builder's changed-files list as a folder tree (with single-child folder chains compacted, like VSCode's Source Control panel) instead of a flat list. Toggleable via the Builders title-bar button." + }, + "codev.backlogShowAll": { + "type": "boolean", + "default": false, + "description": "Show every open backlog issue. When off (the default), the Backlog view filters to items assigned to the current GitHub user. Toggleable via the Backlog title-bar eye icon." } } } diff --git a/packages/vscode/src/__tests__/backlog-filter.test.ts b/packages/vscode/src/__tests__/backlog-filter.test.ts new file mode 100644 index 000000000..cdace04c6 --- /dev/null +++ b/packages/vscode/src/__tests__/backlog-filter.test.ts @@ -0,0 +1,53 @@ +/** + * Unit tests for `filterMine`, the pure helper that powers the + * Backlog view's mine-only / show-all toggle. Lives in `__tests__/` + * (vitest harness) rather than `src/test/` (vscode-test Electron + * harness) because it touches no `vscode` APIs. + */ + +import { describe, it, expect } from 'vitest'; +import type { OverviewBacklogItem } from '@cluesmith/codev-types'; +import { filterMine } from '../views/backlog-filter.js'; + +function assignedItem(id: string, assignees: string[]): OverviewBacklogItem { + return { id, title: `t${id}`, hasBuilder: false, assignees } as unknown as OverviewBacklogItem; +} + +describe('filterMine', () => { + it('keeps only items assigned to currentUser', () => { + const out = filterMine([ + assignedItem('1', ['alice']), + assignedItem('2', ['bob']), + assignedItem('3', ['alice', 'carol']), + ], 'alice'); + expect(out.map(i => i.id)).toEqual(['1', '3']); + }); + + it('returns input unchanged when currentUser is null (gh-unavailable fallback)', () => { + const items = [ + assignedItem('1', ['alice']), + assignedItem('2', []), + ]; + expect(filterMine(items, null).map(i => i.id)).toEqual(['1', '2']); + expect(filterMine(items, undefined).map(i => i.id)).toEqual(['1', '2']); + expect(filterMine(items, '').map(i => i.id)).toEqual(['1', '2']); + }); + + it('matches logins case-insensitively', () => { + const out = filterMine([ + assignedItem('1', ['Alice']), + assignedItem('2', ['BOB']), + ], 'alice'); + expect(out.map(i => i.id)).toEqual(['1']); + }); + + it('returns empty for empty input', () => { + expect(filterMine([], 'alice')).toEqual([]); + }); + + it('drops items with missing assignees field when filtering', () => { + const noAssignees = { id: 'x', title: 'tx', hasBuilder: false } as unknown as OverviewBacklogItem; + const out = filterMine([noAssignees, assignedItem('y', ['alice'])], 'alice'); + expect(out.map(i => i.id)).toEqual(['y']); + }); +}); diff --git a/packages/vscode/src/extension.ts b/packages/vscode/src/extension.ts index a5fc13e3c..bc2a6513c 100644 --- a/packages/vscode/src/extension.ts +++ b/packages/vscode/src/extension.ts @@ -350,6 +350,22 @@ export async function activate(context: vscode.ExtensionContext) { }), ); + // Backlog mine-only / show-all toggle. Default is `false` (mine-only) + // so a fresh install opens to "what's on my plate". Same mechanics as + // the two toggles above: read setting, mirror to context key so the + // paired view-title commands swap correctly, refresh the provider on + // change so the filter takes effect immediately. + const readBacklogShowAll = () => + vscode.workspace.getConfiguration('codev').get('backlogShowAll', false); + vscode.commands.executeCommand('setContext', 'codev.backlogShowAll', readBacklogShowAll()); + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration((e) => { + if (!e.affectsConfiguration('codev.backlogShowAll')) { return; } + vscode.commands.executeCommand('setContext', 'codev.backlogShowAll', readBacklogShowAll()); + backlogProvider.refresh(); + }), + ); + // Periodic overview refresh. VSCode has no timer-based refresh (event-only), // so an idle workspace never sees externally-merged PRs / new issues. Mirror // the dashboard's poll idiom: refresh on a cadence while the Codev sidebar is @@ -629,6 +645,10 @@ export async function activate(context: vscode.ExtensionContext) { vscode.workspace.getConfiguration('codev').update('buildersFileViewAsTree', true, vscode.ConfigurationTarget.Global)), vscode.commands.registerCommand('codev.disableBuildersFileTreeMode', () => vscode.workspace.getConfiguration('codev').update('buildersFileViewAsTree', false, vscode.ConfigurationTarget.Global)), + vscode.commands.registerCommand('codev.showBacklogAll', () => + vscode.workspace.getConfiguration('codev').update('backlogShowAll', true, vscode.ConfigurationTarget.Global)), + vscode.commands.registerCommand('codev.showBacklogMineOnly', () => + vscode.workspace.getConfiguration('codev').update('backlogShowAll', false, vscode.ConfigurationTarget.Global)), vscode.commands.registerCommand('codev.reconnect', () => connectionManager?.reconnect()), vscode.commands.registerCommand('codev.connectTunnel', () => connectTunnel(connectionManager!)), vscode.commands.registerCommand('codev.disconnectTunnel', () => disconnectTunnel(connectionManager!)), diff --git a/packages/vscode/src/views/backlog-filter.ts b/packages/vscode/src/views/backlog-filter.ts new file mode 100644 index 000000000..038c5bd61 --- /dev/null +++ b/packages/vscode/src/views/backlog-filter.ts @@ -0,0 +1,21 @@ +import type { OverviewBacklogItem } from '@cluesmith/codev-types'; + +/** + * Filter a backlog list to items assigned to `currentUser`. If + * `currentUser` is empty / null / undefined (gh unavailable, not + * authenticated), returns the input unchanged so the view doesn't + * collapse to empty when we can't tell who "mine" is. Login matching + * is case-insensitive. + * + * Lives in its own file (not in `backlog.ts`) so vitest unit tests can + * import it without dragging in the `vscode` module. Same pattern the + * codebase uses for any pure helper unit-tested from `__tests__/`. + */ +export function filterMine( + items: OverviewBacklogItem[], + currentUser: string | null | undefined, +): OverviewBacklogItem[] { + const me = currentUser?.toLowerCase(); + if (!me) { return items; } + return items.filter(item => !!item.assignees?.some(a => a.toLowerCase() === me)); +} diff --git a/packages/vscode/src/views/backlog.ts b/packages/vscode/src/views/backlog.ts index febef3921..87222a756 100644 --- a/packages/vscode/src/views/backlog.ts +++ b/packages/vscode/src/views/backlog.ts @@ -5,6 +5,7 @@ import { groupByArea } from '@cluesmith/codev-core/area-grouping'; import type { OverviewCache } from './overview-data.js'; import { BacklogGroupTreeItem, BacklogTreeItem } from './backlog-tree-item.js'; import { AreaGroupExpansionStore } from './area-group-expansion.js'; +import { filterMine } from './backlog-filter.js'; /** * Backlog rows the user can act on — exclude issues that already have an @@ -45,6 +46,15 @@ export class BacklogProvider implements vscode.TreeDataProvider cache.onDidChange(() => this.changeEmitter.fire()); } + /** + * Force a re-render. Used by the `codev.backlogShowAll` config-change + * listener — the cache hasn't changed, but the filter mode has, so + * the tree needs to redraw. + */ + refresh(): void { + this.changeEmitter.fire(); + } + getTreeItem(element: vscode.TreeItem): vscode.TreeItem { return element; } @@ -64,6 +74,19 @@ export class BacklogProvider implements vscode.TreeDataProvider if (!data) { return []; } const items = this.orderedSpawnable(data); + + // Empty mine-only state: the user has the filter on, we know who + // they are, and nothing matched. Show a single non-clickable + // placeholder pointing at the toggle — silent empty view is the + // confusing failure mode the issue called out. + if (items.length === 0 && !readBacklogShowAll() && !!data.currentUser) { + const placeholder = new vscode.TreeItem( + '(no backlog items assigned to you — click the eye icon to see all)', + ); + placeholder.contextValue = 'backlog-empty'; + return [placeholder]; + } + const groups = groupByArea(items, i => i.area); // Degenerate case: a repo that doesn't use `area/*` labels yields a @@ -119,15 +142,27 @@ export class BacklogProvider implements vscode.TreeDataProvider * Spawnable items in display order (mine-first, then rest), preserving * Tower's order within each segment. Identical to the pre-grouping * behavior so within-group ordering matches the old flat list. + * + * If `codev.backlogShowAll` is off and `currentUser` is known, the + * list is further filtered down to items assigned to the current + * user. When `currentUser` is unavailable, `filterMine` is a no-op so + * the view stays useful even when gh can't tell us who the user is. */ private orderedSpawnable(data: NonNullable>): OverviewBacklogItem[] { const me = data.currentUser?.toLowerCase(); const isMine = (item: OverviewBacklogItem) => !!me && !!item.assignees?.some(a => a.toLowerCase() === me); - const items = spawnableBacklog(data.backlog); + let items = spawnableBacklog(data.backlog); + if (!readBacklogShowAll()) { + items = filterMine(items, data.currentUser); + } const mine = items.filter(isMine); const rest = items.filter(item => !isMine(item)); return [...mine, ...rest]; } } + +function readBacklogShowAll(): boolean { + return vscode.workspace.getConfiguration('codev').get('backlogShowAll', false); +} From 063eb09e2a3d3d1acf919c142752e0285d07bd56 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:01:37 +1000 Subject: [PATCH 08/17] [PIR #809] Thread: log implement phase --- codev/state/pir-809_thread.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/codev/state/pir-809_thread.md b/codev/state/pir-809_thread.md index c42915ae9..b48f2ca0f 100644 --- a/codev/state/pir-809_thread.md +++ b/codev/state/pir-809_thread.md @@ -7,3 +7,16 @@ - Filter lives in `BacklogProvider.orderedSpawnable` (the single chokepoint feeding both root and per-group rendering). - Empty-state placeholder renders only when `currentUser` is known AND filter yields zero — avoids confusing the not-yet-loaded path. - Awaiting `plan-approval`. + +## Plan-approval gate +- Approved by architect on 2026-05-28. +- Rebased branch onto fresh `origin/main` (68 commits of main landed under our 4 commits, clean). +- Plan refinement commit landed before phase-transition: corrected test file path from `tests/unit/views/backlog.test.ts` (doesn't exist) to `src/__tests__/backlog-filter.test.ts` after discovering the `src/test/` (vscode-test/Mocha) vs `src/__tests__/` (vitest) separation enforced by `vitest.config.ts`. + +## Implement phase +- Extracted `filterMine` into its own file `packages/vscode/src/views/backlog-filter.ts` — vitest tests in `__tests__/` can't import from `backlog.ts` because it pulls in `vscode`. Standard codebase pattern (mirrors what other pure helpers do). +- `BacklogProvider` reads `codev.backlogShowAll` via a `readBacklogShowAll()` helper at the bottom of `backlog.ts`. Applied in `orderedSpawnable`; placeholder branch added at the top of `rootChildren`. +- Two commands `codev.showBacklogAll` / `codev.showBacklogMineOnly` registered in `extension.ts`; paired `view/title` menu entries in `package.json` use `$(eye)` / `$(eye-closed)` icons. +- `onDidChangeConfiguration` listener mirrors the setting into the `codev.backlogShowAll` context key and refreshes the provider — matches the file-view-as-tree block structure verbatim. +- Checks: `pnpm check-types` ✓, `pnpm lint` ✓, `pnpm test:unit` 100/100 ✓ (5 new), root `pnpm build` ✓, root `pnpm test` 3188/3201 (13 pre-existing skips) ✓. +- Awaiting `dev-approval`. From 1ee3871288ae0e08270518c165756ce5721a73a3 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:02:11 +1000 Subject: [PATCH 09/17] chore(porch): 809 dev-approval gate-requested --- codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index c042999bd..8687a787c 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -11,10 +11,11 @@ gates: approved_at: '2026-05-28T04:40:54.922Z' dev-approval: status: pending + requested_at: '2026-05-28T05:02:11.467Z' pr: status: pending iteration: 1 build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T04:43:18.375Z' +updated_at: '2026-05-28T05:02:11.468Z' From 691cad9f2d4356faee4059adac0de618fd22519b Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:05:37 +1000 Subject: [PATCH 10/17] chore(porch): 809 dev-approval gate-approved --- .../projects/809-vscode-backlog-view-toggle-bet/status.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index 8687a787c..23cf9944c 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -10,12 +10,13 @@ gates: requested_at: '2026-05-28T01:02:09.299Z' approved_at: '2026-05-28T04:40:54.922Z' dev-approval: - status: pending + status: approved requested_at: '2026-05-28T05:02:11.467Z' + approved_at: '2026-05-28T05:05:37.571Z' pr: status: pending iteration: 1 build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T05:02:11.468Z' +updated_at: '2026-05-28T05:05:37.571Z' From 8ef7a48b5d16a4499c4dc2c34d69ae8a6817c8a3 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:05:46 +1000 Subject: [PATCH 11/17] chore(porch): 809 review phase-transition --- codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index 23cf9944c..c80647cff 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -1,7 +1,7 @@ id: '809' title: vscode-backlog-view-toggle-bet protocol: pir -phase: implement +phase: review plan_phases: [] current_plan_phase: null gates: @@ -19,4 +19,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T05:05:37.571Z' +updated_at: '2026-05-28T05:05:46.240Z' From 850f22a1d259941976474cc1ba73cca78343f84b Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:06:43 +1000 Subject: [PATCH 12/17] [PIR #809] Review + retrospective --- .../809-vscode-backlog-view-toggle-bet.md | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 codev/reviews/809-vscode-backlog-view-toggle-bet.md diff --git a/codev/reviews/809-vscode-backlog-view-toggle-bet.md b/codev/reviews/809-vscode-backlog-view-toggle-bet.md new file mode 100644 index 000000000..df33052b7 --- /dev/null +++ b/codev/reviews/809-vscode-backlog-view-toggle-bet.md @@ -0,0 +1,67 @@ +# PIR Review: VS Code Backlog Mine/All Toggle + +Fixes #809 + +## Summary + +The VS Code Backlog view now opens to **mine-only** by default, showing just issues assigned to the current GitHub user. A title-bar eye icon toggles to **show-all** and back, persisting the choice across reloads via the `codev.backlogShowAll` config flag. The implementation reuses the established two-commands-one-flag-paired-when-clauses convention used by `codev.buildersAutoCollapse` and `codev.buildersFileViewAsTree`, so the surface area cost is small and the toggle feels native alongside the existing view-title actions. + +## Files Changed + +- `codev/plans/809-vscode-backlog-view-toggle-bet.md` (+62 / -0) — PIR plan +- `codev/state/pir-809_thread.md` (+22 / -0) — builder thread log +- `packages/vscode/package.json` (+25 / -0) — command, menu, config declarations +- `packages/vscode/src/extension.ts` (+20 / -0) — command registrations, context-key mirror, refresh wiring +- `packages/vscode/src/views/backlog.ts` (+36 / -1) — `refresh()`, filter application, empty-state placeholder, `readBacklogShowAll` +- `packages/vscode/src/views/backlog-filter.ts` (+21 / -0) — new pure helper `filterMine` (vscode-free, so vitest can import it) +- `packages/vscode/src/__tests__/backlog-filter.test.ts` (+53 / -0) — 5 unit tests covering filter behavior + +## Commits + +``` +46def516 [PIR #809] Plan: correct test file path and unit-test scope +f4b10b3c [PIR #809] Plan draft +be662790 [PIR #809] vscode: backlog mine/all toggle +063eb09e [PIR #809] Thread: log implement phase +``` + +(Phase-transition commits authored by porch are omitted from this list — they carry no source changes.) + +## Test Results + +- `pnpm check-types` (vscode): ✓ +- `pnpm lint` (vscode): ✓ +- `pnpm test:unit` (vscode vitest): ✓ 100 tests, 5 new (`filterMine` suite) +- `pnpm build` (root): ✓ +- `pnpm test` (root): ✓ 3188 pass / 13 pre-existing skips +- Manual verification at `dev-approval` gate: approved by the architect after exercising the toggle in a live VS Code window + +## Architecture Updates + +No changes to `codev/resources/arch.md`. This PR adds a view-level toggle to an existing tree provider — it follows the existing convention for `codev.buildersAutoCollapse` / `codev.buildersFileViewAsTree` and introduces no new architectural pattern. The arch doc already explains the broader sidebar architecture; a third instance of the same pattern doesn't change that description. + +## Lessons Learned Updates + +No changes to `codev/resources/lessons-learned.md`. The mechanics here (two commands + one config flag + setContext mirror + provider refresh) are already established in two prior callers; this is the third use of the same pattern, not a new lesson. If a fourth-or-fifth use lands later, that may be the right time to encode "toggle convention" as an explicit lesson — but a single feature implementing an existing pattern doesn't merit a global lessons entry. + +## Things to Look At During PR Review + +- **`filterMine` lives in its own file** (`views/backlog-filter.ts`) rather than next to `spawnableBacklog` in `backlog.ts`. The reason is purely test ergonomics: vitest tests in `src/__tests__/` cannot import from any module that pulls in the `vscode` runtime, and `backlog.ts` does (`import * as vscode from 'vscode'`). Other pure helpers in the codebase live in `@cluesmith/codev-core` for the same reason; an in-package file keeps this one's scope narrow (only the Backlog view uses it). +- **The empty-state placeholder gate is triple-guarded**: `items.length === 0 && !readBacklogShowAll() && !!data.currentUser`. The third clause matters — without it, a user who isn't signed into `gh` could see "no items assigned to you" even though we don't actually know who they are. The filter itself is a no-op in that case (returns all items), but the placeholder branch was a separate decision and needed its own guard. +- **The icon-swap behavior in `makeRow` is unchanged.** In show-all mode the `account` icon still distinguishes assigned items from the rest, per the issue's "no regression" criterion. In mine-only mode every visible row is by definition assigned-to-you, so the `account` icon is redundant-but-informative — acceptable and consistent. +- **Vitest pickup confirmed**: `vitest.config.ts:16` only includes `src/__tests__/**`, not `src/test/**`. My plan originally pointed at the wrong test directory; a plan-refinement commit (`46def516`) landed before the implement phase to correct it. + +## How to Test Locally + +For reviewers pulling the branch: + +- **View diff**: VS Code sidebar → right-click builder `pir-809` → **View Diff** +- **Run dev server**: VS Code sidebar → right-click builder → **Run Dev Server**, or `afx dev pir-809` +- **What to verify** (mapped from the plan's Test Plan): + 1. Default state: Backlog view shows only items assigned to you, eye icon is `$(eye)` + 2. Click the eye → all items appear, icon flips to `$(eye-closed)`, the `account` icon still marks your items + 3. Click again → filtered back to yours + 4. Reload window — toggle state persists (Global config target) + 5. With zero assigned items and mine-only active, the placeholder row appears with the text `(no backlog items assigned to you — click the eye icon to see all)` + 6. If `gh` auth is broken so `currentUser` is null, the view falls back to showing all items regardless of toggle position + 7. Right-click context menu and click-to-view behavior on rows are unchanged From daa0371f3a0e395f7ac12e04f4aa422a841a63db Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:07:00 +1000 Subject: [PATCH 13/17] chore(porch): 809 record PR #910 --- .../809-vscode-backlog-view-toggle-bet/status.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index c80647cff..f095ba7e5 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -19,4 +19,9 @@ iteration: 1 build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T05:05:46.240Z' +updated_at: '2026-05-28T05:07:00.246Z' +pr_history: + - phase: review + pr_number: 910 + branch: builder/pir-809 + created_at: '2026-05-28T05:07:00.246Z' From 3c44380ce7136eb3b539c96b05cd14e085ada3e5 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:07:09 +1000 Subject: [PATCH 14/17] chore(porch): 809 review build-complete --- codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index f095ba7e5..0827fb744 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -16,10 +16,10 @@ gates: pr: status: pending iteration: 1 -build_complete: false +build_complete: true history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T05:07:00.246Z' +updated_at: '2026-05-28T05:07:09.434Z' pr_history: - phase: review pr_number: 910 From dd71a4c6a4060ae9f178c3dfd86c9a1388943244 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:18:21 +1000 Subject: [PATCH 15/17] chore(porch): 809 pr gate-requested --- .../projects/809-vscode-backlog-view-toggle-bet/status.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index 0827fb744..87e887ab3 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -15,13 +15,15 @@ gates: approved_at: '2026-05-28T05:05:37.571Z' pr: status: pending + requested_at: '2026-05-28T05:18:21.430Z' iteration: 1 -build_complete: true +build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T05:07:09.434Z' +updated_at: '2026-05-28T05:18:21.431Z' pr_history: - phase: review pr_number: 910 branch: builder/pir-809 created_at: '2026-05-28T05:07:00.246Z' +pr_ready_for_human: true From bc15c6cf0befb6c15579a64757a3d459d6afb292 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:19:23 +1000 Subject: [PATCH 16/17] chore(porch): 809 pr gate-approved --- .../809-vscode-backlog-view-toggle-bet/status.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index 87e887ab3..8ddf20886 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -14,16 +14,17 @@ gates: requested_at: '2026-05-28T05:02:11.467Z' approved_at: '2026-05-28T05:05:37.571Z' pr: - status: pending + status: approved requested_at: '2026-05-28T05:18:21.430Z' + approved_at: '2026-05-28T05:19:23.568Z' iteration: 1 build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T05:18:21.431Z' +updated_at: '2026-05-28T05:19:23.569Z' pr_history: - phase: review pr_number: 910 branch: builder/pir-809 created_at: '2026-05-28T05:07:00.246Z' -pr_ready_for_human: true +pr_ready_for_human: false From 8c46865008a0515c5f950f078fcc59512ee6076c Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Thu, 28 May 2026 15:19:34 +1000 Subject: [PATCH 17/17] chore(porch): 809 protocol complete --- codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml index 8ddf20886..f27431e36 100644 --- a/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml +++ b/codev/projects/809-vscode-backlog-view-toggle-bet/status.yaml @@ -1,7 +1,7 @@ id: '809' title: vscode-backlog-view-toggle-bet protocol: pir -phase: review +phase: verified plan_phases: [] current_plan_phase: null gates: @@ -21,7 +21,7 @@ iteration: 1 build_complete: false history: [] started_at: '2026-05-28T00:59:34.520Z' -updated_at: '2026-05-28T05:19:23.569Z' +updated_at: '2026-05-28T05:19:34.835Z' pr_history: - phase: review pr_number: 910