diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index 4cf810d8..ebb67c00 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -3435,3 +3435,4 @@ SCOPE CLARIFICATION (2026-06-10, from user): - Net behaviour: a fan-out of N agents -> N distinct collapsed cards, each containing its own complete run; surrounding non-agent tool calls still group into their normal Activity card. Test additions: (a) two concurrent agents whose sidechain items interleave -> each agent''s run items land under its own card, none cross-attributed; (b) an unattributable sidechain item (no parent_tool_use_id, broken chain) is NOT swept into the nearest agent''s card; (c) regression: consecutive Bash/Read calls still form one Activity cluster.', NULL, '2026-06-10 15:28:52', '2026-06-10 15:28:52', '2026-06-10 15:28:52', NULL, '0352b24d88a4bf24ede628880fe9b502', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4FDREHRYRR7B9ER72KCQKC', 'status', 'backlog', 'in_progress', NULL, '2026-06-10 15:54:23', '2026-06-10 15:54:23', '2026-06-10 15:54:23', NULL, '8061a1d449acdb1e467239de0dce5d1d', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index 51a4d744..3bef95e0 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -2903,3 +2903,59 @@ SCOPE CLARIFICATION (2026-06-10, from user): - Net behaviour: a fan-out of N agents -> N distinct collapsed cards, each containing its own complete run; surrounding non-agent tool calls still group into their normal Activity card. Test additions: (a) two concurrent agents whose sidechain items interleave -> each agent''s run items land under its own card, none cross-attributed; (b) an unattributable sidechain item (no parent_tool_use_id, broken chain) is NOT swept into the nearest agent''s card; (c) regression: consecutive Bash/Read calls still form one Activity cluster.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-10 15:26:33', '2026-06-10 15:28:52', NULL, '05bc2daa013de8cf163cdd54c6014f07', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4FDREHRYRR7B9ER72KCQKC', 'story', '06FB0TNQM5TWC00GW0P3X02HZW', 'Ticket panel: per-type filter chips (Bug / Ticket / Epic / Initiative)', 'Add a row of type-filter toggle chips at the top of the tickets panel, directly below the "Filter tickets…" box (no section header — the chips read on their own). One chip per ticket type the user thinks in: Bug, Ticket, Epic, Initiative. All four ON by default. + +## Behaviour +- Single-click a chip → toggle that type in/out of the list. +- Double-click a chip → isolate (solo) that type: turns it ON and all others OFF. Double-click the same chip again → restore all to ON. This is the chart-legend solo pattern (Plotly/Tableau/Grafana) — learnable and fully reversible. +- Last-off resets to all-on: disabling the final remaining type snaps all chips back ON. An empty type filter means "no filter", so the list is never mysteriously blank. +- Tooltip per chip: "Click to toggle · double-click to isolate". + +## Type mapping (pql → chip) +pql ticket types are initiative, epic, story, task, bug (see lib/builtin/tickets/src/ticket_colors.dart). The four chips map as: +- Bug → bug +- Ticket → story + task (leaf work items) +- Epic → epic +- Initiative → initiative + +Each chip carries its type-colored dot + border using TicketTypeColors (bug #E87D7D, story/task green/grey, epic #78A0F8, initiative #C792EA). + +## Filtering +- Type filter is ANDed with the existing text filter in _TicketsViewState (lib/builtin/tickets/src/tickets_view.dart): a ticket shows only if its type is enabled AND it matches the text filter. +- When the type filter hides all items in a status section, that section collapses out (same as text-filter behaviour today). + +## Implementation notes +- Active-chip visual: filled tint + type-colored border (active) vs muted/no border (inactive) — reuse the _Toggle pattern from lib/builtin/search/src/search_panel_view.dart and ClideTappable. +- Persist nothing across sessions for v1 (always all-on on load); revisit if requested. +- a11y: Semantics(button, toggled) per chip, mirroring the search-panel toggle. + +## Wireframe +docs/design/wireframes/tickets/ticket-type-filters.json (+ .png export)', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-10 15:54:09', '2026-06-10 15:54:09', NULL, '3e7c03bbb536a4c2d45f770abfee8b13', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4FDREHRYRR7B9ER72KCQKC', 'story', '06FB0TNQM5TWC00GW0P3X02HZW', 'Ticket panel: per-type filter chips (Bug / Ticket / Epic / Initiative)', 'Add a row of type-filter toggle chips at the top of the tickets panel, directly below the "Filter tickets…" box (no section header — the chips read on their own). One chip per ticket type the user thinks in: Bug, Ticket, Epic, Initiative. All four ON by default. + +## Behaviour +- Single-click a chip → toggle that type in/out of the list. +- Double-click a chip → isolate (solo) that type: turns it ON and all others OFF. Double-click the same chip again → restore all to ON. This is the chart-legend solo pattern (Plotly/Tableau/Grafana) — learnable and fully reversible. +- Last-off resets to all-on: disabling the final remaining type snaps all chips back ON. An empty type filter means "no filter", so the list is never mysteriously blank. +- Tooltip per chip: "Click to toggle · double-click to isolate". + +## Type mapping (pql → chip) +pql ticket types are initiative, epic, story, task, bug (see lib/builtin/tickets/src/ticket_colors.dart). The four chips map as: +- Bug → bug +- Ticket → story + task (leaf work items) +- Epic → epic +- Initiative → initiative + +Each chip carries its type-colored dot + border using TicketTypeColors (bug #E87D7D, story/task green/grey, epic #78A0F8, initiative #C792EA). + +## Filtering +- Type filter is ANDed with the existing text filter in _TicketsViewState (lib/builtin/tickets/src/tickets_view.dart): a ticket shows only if its type is enabled AND it matches the text filter. +- When the type filter hides all items in a status section, that section collapses out (same as text-filter behaviour today). + +## Implementation notes +- Active-chip visual: filled tint + type-colored border (active) vs muted/no border (inactive) — reuse the _Toggle pattern from lib/builtin/search/src/search_panel_view.dart and ClideTappable. +- Persist nothing across sessions for v1 (always all-on on load); revisit if requested. +- a11y: Semantics(button, toggled) per chip, mirroring the search-panel toggle. + +## Wireframe +docs/design/wireframes/tickets/ticket-type-filters.json (+ .png export)', 'in_progress', 'medium', NULL, NULL, NULL, '2026-06-10 15:54:09', '2026-06-10 15:54:23', NULL, 'f5614424c00196aabab868db75483bf2', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); diff --git a/docs/design/wireframes/tickets/ticket-type-filters.json b/docs/design/wireframes/tickets/ticket-type-filters.json new file mode 100644 index 00000000..e25aafc9 --- /dev/null +++ b/docs/design/wireframes/tickets/ticket-type-filters.json @@ -0,0 +1,230 @@ +{ + "name": "Ticket Panel — Type Filters", + "shapes": { + "panel": { + "type": "Rectangle", + "left": 80, "top": 60, "width": 400, "height": 520, + "fillColor": "#0d0d11", + "strokeColor": "#23232b", + "corners": [6, 6, 6, 6] + }, + + "filter-box": { + "type": "Rectangle", + "parent": "panel", + "left": 96, "top": 76, "width": 368, "height": 30, + "fillColor": "#16161c", + "strokeColor": "#2c2c36", + "corners": [5, 5, 5, 5] + }, + "filter-icon": { + "type": "Text", + "parent": "filter-box", + "left": 108, "top": 84, + "text": "⌕", + "fontColor": "#5b5b66", + "fontSize": 13 + }, + "filter-text": { + "type": "Text", + "parent": "filter-box", + "left": 126, "top": 85, + "text": "Filter tickets…", + "fontColor": "#5b5b66", + "fontSize": 12 + }, + "refresh-icon": { + "type": "Text", + "parent": "filter-box", + "left": 442, "top": 84, + "text": "↻", + "fontColor": "#5b5b66", + "fontSize": 13 + }, + + "chip-bug": { + "type": "Rectangle", + "parent": "panel", + "left": 96, "top": 118, "width": 66, "height": 24, + "fillColor": "#211519", + "strokeColor": "#E87D7D", + "corners": [4, 4, 4, 4] + }, + "chip-bug-dot": { + "type": "Ellipse", + "parent": "chip-bug", + "left": 104, "top": 126, "width": 8, "height": 8, + "fillColor": "#E87D7D", + "strokeColor": "#E87D7D" + }, + "chip-bug-label": { + "type": "Text", + "parent": "chip-bug", + "left": 117, "top": 124, + "text": "Bug", + "fontColor": "#e8e8ee", + "fontSize": 11 + }, + + "chip-ticket": { + "type": "Rectangle", + "parent": "panel", + "left": 168, "top": 118, "width": 78, "height": 24, + "fillColor": "#15191d", + "strokeColor": "#7DD3A8", + "corners": [4, 4, 4, 4] + }, + "chip-ticket-dot": { + "type": "Ellipse", + "parent": "chip-ticket", + "left": 176, "top": 126, "width": 8, "height": 8, + "fillColor": "#7DD3A8", + "strokeColor": "#7DD3A8" + }, + "chip-ticket-label": { + "type": "Text", + "parent": "chip-ticket", + "left": 189, "top": 124, + "text": "Ticket", + "fontColor": "#e8e8ee", + "fontSize": 11 + }, + + "chip-epic": { + "type": "Rectangle", + "parent": "panel", + "left": 252, "top": 118, "width": 68, "height": 24, + "fillColor": "#14171f", + "strokeColor": "#78A0F8", + "corners": [4, 4, 4, 4] + }, + "chip-epic-dot": { + "type": "Ellipse", + "parent": "chip-epic", + "left": 260, "top": 126, "width": 8, "height": 8, + "fillColor": "#78A0F8", + "strokeColor": "#78A0F8" + }, + "chip-epic-label": { + "type": "Text", + "parent": "chip-epic", + "left": 273, "top": 124, + "text": "Epic", + "fontColor": "#e8e8ee", + "fontSize": 11 + }, + + "chip-init": { + "type": "Rectangle", + "parent": "panel", + "left": 326, "top": 118, "width": 86, "height": 24, + "fillColor": "#1a151f", + "strokeColor": "#C792EA", + "corners": [4, 4, 4, 4] + }, + "chip-init-dot": { + "type": "Ellipse", + "parent": "chip-init", + "left": 334, "top": 126, "width": 8, "height": 8, + "fillColor": "#C792EA", + "strokeColor": "#C792EA" + }, + "chip-init-label": { + "type": "Text", + "parent": "chip-init", + "left": 347, "top": 124, + "text": "Initiative", + "fontColor": "#e8e8ee", + "fontSize": 11 + }, + + "divider": { + "type": "Line", + "parent": "panel", + "left": 96, "top": 158, "width": 368, "height": 1, + "strokeColor": "#23232b" + }, + + "section-header": { + "type": "Text", + "parent": "panel", + "left": 98, "top": 172, + "text": "▾ BACKLOG · 61", + "fontColor": "#6b6b76", + "fontSize": 10 + }, + + "card-1": { + "type": "Rectangle", + "parent": "panel", + "left": 96, "top": 192, "width": 368, "height": 56, + "fillColor": "#0d0d11", + "strokeColor": "#23232b", + "corners": [4, 4, 4, 4] + }, + "card-1-dot": { + "type": "Ellipse", + "parent": "card-1", + "left": 108, "top": 204, "width": 8, "height": 8, + "fillColor": "#C792EA", + "strokeColor": "#C792EA" + }, + "card-1-id": { + "type": "Text", + "parent": "card-1", + "left": 122, "top": 200, + "text": "T-8", + "fontColor": "#e8e8ee", + "fontSize": 11 + }, + "card-1-title": { + "type": "Text", + "parent": "card-1", + "left": 108, "top": 220, + "text": "Tier 6 — extension API, settings, theming, builds", + "fontColor": "#b8b8c2", + "fontSize": 11 + }, + + "card-2": { + "type": "Rectangle", + "parent": "panel", + "left": 96, "top": 254, "width": 368, "height": 56, + "fillColor": "#0d0d11", + "strokeColor": "#23232b", + "corners": [4, 4, 4, 4] + }, + "card-2-dot": { + "type": "Ellipse", + "parent": "card-2", + "left": 108, "top": 266, "width": 8, "height": 8, + "fillColor": "#E87D7D", + "strokeColor": "#E87D7D" + }, + "card-2-id": { + "type": "Text", + "parent": "card-2", + "left": 122, "top": 262, + "text": "T-19", + "fontColor": "#e8e8ee", + "fontSize": 11 + }, + "card-2-title": { + "type": "Text", + "parent": "card-2", + "left": 108, "top": 282, + "text": "Filter box loses focus on refresh tick", + "fontColor": "#b8b8c2", + "fontSize": 11 + }, + + "caption": { + "type": "Text", + "parent": "panel", + "left": 96, "top": 540, + "text": "All four type filters ON by default — tap a chip to toggle that type out.", + "fontColor": "#56565f", + "fontSize": 10 + } + } +} diff --git a/docs/design/wireframes/tickets/ticket-type-filters.png b/docs/design/wireframes/tickets/ticket-type-filters.png new file mode 100644 index 00000000..e92fd382 Binary files /dev/null and b/docs/design/wireframes/tickets/ticket-type-filters.png differ