From b8c8826b9e501f29e389e24870e9d4de865c38d7 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 24 Apr 2026 10:54:17 +0200 Subject: [PATCH] use pql --with-context for ticket detail loading Replaces the N+1 parent-chain walk (one IPC call per ancestor, one per decision ref) with a single pql ticket show --with-context call. pql now returns ancestors and decisions arrays inline. Updates PqlClient.ticketShow to accept withContext/withChildren flags, removes the obsolete withDecision flag. Co-Authored-By: Claude --- .../tickets/src/ticket_detail_controller.dart | 31 +++---------------- lib/src/daemon/pql_commands.dart | 3 +- lib/src/pql/client.dart | 8 ++--- 3 files changed, 10 insertions(+), 32 deletions(-) diff --git a/lib/builtin/tickets/src/ticket_detail_controller.dart b/lib/builtin/tickets/src/ticket_detail_controller.dart index 451a6d0f..46e7950d 100644 --- a/lib/builtin/tickets/src/ticket_detail_controller.dart +++ b/lib/builtin/tickets/src/ticket_detail_controller.dart @@ -49,7 +49,7 @@ class TicketDetailController extends ChangeNotifier { _loading = true; notifyListeners(); - final resp = await ipc.request('pql.tickets.show', args: {'id': id}); + final resp = await ipc.request('pql.tickets.show', args: {'id': id, 'withContext': true}); if (!resp.ok) { _loading = false; notifyListeners(); @@ -57,33 +57,10 @@ class TicketDetailController extends ChangeNotifier { } final ticket = resp.data; - final parents = >[]; - final decisions = >[]; + final ancestors = (ticket['ancestors'] as List?)?.cast>() ?? const []; + final decisions = (ticket['decisions'] as List?)?.cast>() ?? const []; - // Walk parent chain - var pid = ticket['parent_id'] as String?; - while (pid != null) { - final pr = await ipc.request('pql.tickets.show', args: {'id': pid}); - if (!pr.ok) break; - parents.add(pr.data); - pid = pr.data['parent_id'] as String?; - } - - // Collect decision refs from ticket and parents - final refs = {}; - final dr = ticket['decision_ref'] as String?; - if (dr != null) refs.add(dr); - for (final p in parents) { - final pdr = p['decision_ref'] as String?; - if (pdr != null) refs.add(pdr); - } - - for (final ref in refs) { - final dr = await ipc.request('pql.decisions.show', args: {'id': ref}); - if (dr.ok) decisions.add(dr.data); - } - - _detail = TicketDetail(ticket: ticket, parents: parents, decisions: decisions); + _detail = TicketDetail(ticket: ticket, parents: ancestors, decisions: decisions); _loading = false; messages.publish('builtin.tickets', 'focus', {'id': id}); notifyListeners(); diff --git a/lib/src/daemon/pql_commands.dart b/lib/src/daemon/pql_commands.dart index da731669..9be0b39d 100644 --- a/lib/src/daemon/pql_commands.dart +++ b/lib/src/daemon/pql_commands.dart @@ -200,8 +200,9 @@ void registerPqlCommands(DaemonDispatcher d, PqlClient pql) { try { final result = await pql.ticketShow( id, - withDecision: req.args['withDecision'] as bool? ?? false, + withContext: req.args['withContext'] as bool? ?? false, withBlockers: req.args['withBlockers'] as bool? ?? false, + withChildren: req.args['withChildren'] as bool? ?? false, ); return IpcResponse.ok(id: req.id, data: result); } on PqlException catch (e) { diff --git a/lib/src/pql/client.dart b/lib/src/pql/client.dart index 6f0079a7..10329021 100644 --- a/lib/src/pql/client.dart +++ b/lib/src/pql/client.dart @@ -8,8 +8,6 @@ library; import 'dart:convert'; import 'dart:io'; -import 'package:flutter/foundation.dart'; - class PqlException implements Exception { const PqlException(this.message, {this.exitCode = 1, this.stderr = ''}); final String message; @@ -126,12 +124,14 @@ class PqlClient { Future> ticketShow( String id, { - bool withDecision = false, + bool withContext = false, bool withBlockers = false, + bool withChildren = false, }) async { final args = ['ticket', 'show', id]; - if (withDecision) args.add('--with-decision'); + if (withContext) args.add('--with-context'); if (withBlockers) args.add('--with-blockers'); + if (withChildren) args.add('--with-children'); return _runObject(args); }