nyk commited on
Commit
1ec4113
·
unverified ·
1 Parent(s): 142b423

feat: add awaiting_owner task status detection (#428)

Browse files

Add awaiting_owner column to task board with keyword-based detection
for tasks requiring human action. Tasks matching keywords like
"waiting for", "needs human", "approval needed" are automatically
placed in a dedicated column with orange styling.

Supersedes #397

messages/ar.json CHANGED
@@ -593,6 +593,7 @@
593
  "columnAriaLabel": "عمود {title}، {count} مهمة",
594
  "colInbox": "البريد الوارد",
595
  "colAssigned": "مُخصّص",
 
596
  "colInProgress": "قيد التنفيذ",
597
  "colReview": "المراجعة",
598
  "colQualityReview": "مراجعة الجودة",
 
593
  "columnAriaLabel": "عمود {title}، {count} مهمة",
594
  "colInbox": "البريد الوارد",
595
  "colAssigned": "مُخصّص",
596
+ "colAwaitingOwner": "بانتظار المالك",
597
  "colInProgress": "قيد التنفيذ",
598
  "colReview": "المراجعة",
599
  "colQualityReview": "مراجعة الجودة",
messages/de.json CHANGED
@@ -593,6 +593,7 @@
593
  "columnAriaLabel": "Spalte {title}, {count} Aufgaben",
594
  "colInbox": "Posteingang",
595
  "colAssigned": "Zugewiesen",
 
596
  "colInProgress": "In Bearbeitung",
597
  "colReview": "Überprüfung",
598
  "colQualityReview": "Qualitätsprüfung",
 
593
  "columnAriaLabel": "Spalte {title}, {count} Aufgaben",
594
  "colInbox": "Posteingang",
595
  "colAssigned": "Zugewiesen",
596
+ "colAwaitingOwner": "Wartet auf Besitzer",
597
  "colInProgress": "In Bearbeitung",
598
  "colReview": "Überprüfung",
599
  "colQualityReview": "Qualitätsprüfung",
messages/en.json CHANGED
@@ -742,6 +742,7 @@
742
  "columnAriaLabel": "{title} column, {count} tasks",
743
  "colInbox": "Inbox",
744
  "colAssigned": "Assigned",
 
745
  "colInProgress": "In Progress",
746
  "colReview": "Review",
747
  "colQualityReview": "Quality Review",
 
742
  "columnAriaLabel": "{title} column, {count} tasks",
743
  "colInbox": "Inbox",
744
  "colAssigned": "Assigned",
745
+ "colAwaitingOwner": "Awaiting Owner",
746
  "colInProgress": "In Progress",
747
  "colReview": "Review",
748
  "colQualityReview": "Quality Review",
messages/es.json CHANGED
@@ -593,6 +593,7 @@
593
  "columnAriaLabel": "Columna {title}, {count} tareas",
594
  "colInbox": "Bandeja de entrada",
595
  "colAssigned": "Asignado",
 
596
  "colInProgress": "En progreso",
597
  "colReview": "Revisión",
598
  "colQualityReview": "Revisión de calidad",
 
593
  "columnAriaLabel": "Columna {title}, {count} tareas",
594
  "colInbox": "Bandeja de entrada",
595
  "colAssigned": "Asignado",
596
+ "colAwaitingOwner": "Esperando al propietario",
597
  "colInProgress": "En progreso",
598
  "colReview": "Revisión",
599
  "colQualityReview": "Revisión de calidad",
messages/fr.json CHANGED
@@ -593,6 +593,7 @@
593
  "columnAriaLabel": "Colonne {title}, {count} tâches",
594
  "colInbox": "Boîte de réception",
595
  "colAssigned": "Assigné",
 
596
  "colInProgress": "En cours",
597
  "colReview": "Révision",
598
  "colQualityReview": "Révision qualité",
 
593
  "columnAriaLabel": "Colonne {title}, {count} tâches",
594
  "colInbox": "Boîte de réception",
595
  "colAssigned": "Assigné",
596
+ "colAwaitingOwner": "En attente du propriétaire",
597
  "colInProgress": "En cours",
598
  "colReview": "Révision",
599
  "colQualityReview": "Révision qualité",
messages/ja.json CHANGED
@@ -593,6 +593,7 @@
593
  "columnAriaLabel": "{title}列、{count}件のタスク",
594
  "colInbox": "受信トレイ",
595
  "colAssigned": "割り当て済み",
 
596
  "colInProgress": "進行中",
597
  "colReview": "レビュー",
598
  "colQualityReview": "品質レビュー",
 
593
  "columnAriaLabel": "{title}列、{count}件のタスク",
594
  "colInbox": "受信トレイ",
595
  "colAssigned": "割り当て済み",
596
+ "colAwaitingOwner": "オーナー待ち",
597
  "colInProgress": "進行中",
598
  "colReview": "レビュー",
599
  "colQualityReview": "品質レビュー",
messages/ko.json CHANGED
@@ -593,6 +593,7 @@
593
  "columnAriaLabel": "{title} 열, {count}개 작업",
594
  "colInbox": "받은 편지함",
595
  "colAssigned": "할당됨",
 
596
  "colInProgress": "진행 중",
597
  "colReview": "검토",
598
  "colQualityReview": "품질 검토",
 
593
  "columnAriaLabel": "{title} 열, {count}개 작업",
594
  "colInbox": "받은 편지함",
595
  "colAssigned": "할당됨",
596
+ "colAwaitingOwner": "소유자 대기 중",
597
  "colInProgress": "진행 중",
598
  "colReview": "검토",
599
  "colQualityReview": "품질 검토",
messages/pt.json CHANGED
@@ -593,6 +593,7 @@
593
  "columnAriaLabel": "Coluna {title}, {count} tarefas",
594
  "colInbox": "Caixa de entrada",
595
  "colAssigned": "Atribuído",
 
596
  "colInProgress": "Em andamento",
597
  "colReview": "Revisão",
598
  "colQualityReview": "Revisão de qualidade",
 
593
  "columnAriaLabel": "Coluna {title}, {count} tarefas",
594
  "colInbox": "Caixa de entrada",
595
  "colAssigned": "Atribuído",
596
+ "colAwaitingOwner": "Aguardando proprietário",
597
  "colInProgress": "Em andamento",
598
  "colReview": "Revisão",
599
  "colQualityReview": "Revisão de qualidade",
messages/ru.json CHANGED
@@ -593,6 +593,7 @@
593
  "columnAriaLabel": "Колонка {title}, {count} задач",
594
  "colInbox": "Входящие",
595
  "colAssigned": "Назначено",
 
596
  "colInProgress": "В работе",
597
  "colReview": "На проверке",
598
  "colQualityReview": "Контроль качества",
 
593
  "columnAriaLabel": "Колонка {title}, {count} задач",
594
  "colInbox": "Входящие",
595
  "colAssigned": "Назначено",
596
+ "colAwaitingOwner": "Ожидает владельца",
597
  "colInProgress": "В работе",
598
  "colReview": "На проверке",
599
  "colQualityReview": "Контроль качества",
messages/zh.json CHANGED
@@ -831,6 +831,7 @@
831
  "columnAriaLabel": "{title} 列,{count} 个任务",
832
  "colInbox": "收件箱",
833
  "colAssigned": "已分配",
 
834
  "colInProgress": "进行中",
835
  "colReview": "审查",
836
  "colQualityReview": "质量审查",
 
831
  "columnAriaLabel": "{title} 列,{count} 个任务",
832
  "colInbox": "收件箱",
833
  "colAssigned": "已分配",
834
+ "colAwaitingOwner": "等待所有者",
835
  "colInProgress": "进行中",
836
  "colReview": "审查",
837
  "colQualityReview": "质量审查",
src/components/panels/task-board-panel.tsx CHANGED
@@ -22,7 +22,7 @@ interface Task {
22
  id: number
23
  title: string
24
  description?: string
25
- status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done'
26
  priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
27
  assigned_to?: string
28
  created_by: string
@@ -89,12 +89,27 @@ interface MentionOption {
89
  const STATUS_COLUMN_KEYS = [
90
  { key: 'inbox', titleKey: 'colInbox', color: 'bg-secondary text-foreground' },
91
  { key: 'assigned', titleKey: 'colAssigned', color: 'bg-blue-500/20 text-blue-400' },
 
92
  { key: 'in_progress', titleKey: 'colInProgress', color: 'bg-yellow-500/20 text-yellow-400' },
93
  { key: 'review', titleKey: 'colReview', color: 'bg-purple-500/20 text-purple-400' },
94
  { key: 'quality_review', titleKey: 'colQualityReview', color: 'bg-indigo-500/20 text-indigo-400' },
95
  { key: 'done', titleKey: 'colDone', color: 'bg-green-500/20 text-green-400' },
96
  ]
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  /** Build a human-readable label for a session key like "agent:nefes:telegram-group-123" */
99
  function formatSessionLabel(s: { key: string; channel?: string; kind?: string; label?: string }): string {
100
  if (s.label) return s.label
@@ -535,9 +550,12 @@ export function TaskBoardPanel() {
535
  // Poll as SSE fallback — pauses when SSE is delivering events
536
  useSmartPoll(fetchData, 30000, { pauseWhenSseConnected: true })
537
 
538
- // Group tasks by status
539
  const tasksByStatus = statusColumns.reduce((acc, column) => {
540
- acc[column.key] = tasks.filter(task => task.status === column.key)
 
 
 
541
  return acc
542
  }, {} as Record<string, Task[]>)
543
 
@@ -1017,6 +1035,11 @@ export function TaskBoardPanel() {
1017
  Aegis
1018
  </span>
1019
  )}
 
 
 
 
 
1020
  </div>
1021
  </div>
1022
  </div>
@@ -1820,6 +1843,7 @@ function ClaudeCodeTasksSection() {
1820
  s === 'completed' ? 'text-green-400' :
1821
  s === 'in_progress' ? 'text-blue-400' :
1822
  s === 'blocked' ? 'text-red-400' :
 
1823
  'text-muted-foreground'
1824
 
1825
  return (
 
22
  id: number
23
  title: string
24
  description?: string
25
+ status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done' | 'awaiting_owner'
26
  priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
27
  assigned_to?: string
28
  created_by: string
 
89
  const STATUS_COLUMN_KEYS = [
90
  { key: 'inbox', titleKey: 'colInbox', color: 'bg-secondary text-foreground' },
91
  { key: 'assigned', titleKey: 'colAssigned', color: 'bg-blue-500/20 text-blue-400' },
92
+ { key: 'awaiting_owner', titleKey: 'colAwaitingOwner', color: 'bg-orange-500/20 text-orange-400' },
93
  { key: 'in_progress', titleKey: 'colInProgress', color: 'bg-yellow-500/20 text-yellow-400' },
94
  { key: 'review', titleKey: 'colReview', color: 'bg-purple-500/20 text-purple-400' },
95
  { key: 'quality_review', titleKey: 'colQualityReview', color: 'bg-indigo-500/20 text-indigo-400' },
96
  { key: 'done', titleKey: 'colDone', color: 'bg-green-500/20 text-green-400' },
97
  ]
98
 
99
+ const AWAITING_OWNER_KEYWORDS = [
100
+ 'waiting for', 'waiting on', 'needs human', 'manual action',
101
+ 'account creation', 'browser login', 'approval needed',
102
+ 'owner action', 'human required', 'blocked on owner',
103
+ 'awaiting owner', 'awaiting human', 'needs owner',
104
+ ]
105
+
106
+ function detectAwaitingOwner(task: Task): boolean {
107
+ if (task.status === 'awaiting_owner') return true
108
+ if (task.status !== 'assigned' && task.status !== 'in_progress') return false
109
+ const text = `${task.title} ${task.description || ''}`.toLowerCase()
110
+ return AWAITING_OWNER_KEYWORDS.some(kw => text.includes(kw))
111
+ }
112
+
113
  /** Build a human-readable label for a session key like "agent:nefes:telegram-group-123" */
114
  function formatSessionLabel(s: { key: string; channel?: string; kind?: string; label?: string }): string {
115
  if (s.label) return s.label
 
550
  // Poll as SSE fallback — pauses when SSE is delivering events
551
  useSmartPoll(fetchData, 30000, { pauseWhenSseConnected: true })
552
 
553
+ // Group tasks by status, overriding for awaiting_owner detection
554
  const tasksByStatus = statusColumns.reduce((acc, column) => {
555
+ acc[column.key] = tasks.filter(task => {
556
+ const effectiveStatus = detectAwaitingOwner(task) ? 'awaiting_owner' : task.status
557
+ return effectiveStatus === column.key
558
+ })
559
  return acc
560
  }, {} as Record<string, Task[]>)
561
 
 
1035
  Aegis
1036
  </span>
1037
  )}
1038
+ {detectAwaitingOwner(task) && (
1039
+ <span className="text-[10px] px-1.5 py-0.5 rounded bg-orange-500/20 text-orange-400 border border-orange-500/30 font-mono">
1040
+ {t('colAwaitingOwner')}
1041
+ </span>
1042
+ )}
1043
  </div>
1044
  </div>
1045
  </div>
 
1843
  s === 'completed' ? 'text-green-400' :
1844
  s === 'in_progress' ? 'text-blue-400' :
1845
  s === 'blocked' ? 'text-red-400' :
1846
+ s === 'awaiting_owner' ? 'text-orange-400' :
1847
  'text-muted-foreground'
1848
 
1849
  return (
src/store/index.ts CHANGED
@@ -100,7 +100,7 @@ export interface Task {
100
  id: number
101
  title: string
102
  description?: string
103
- status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done'
104
  priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
105
  project_id?: number
106
  project_ticket_no?: number
 
100
  id: number
101
  title: string
102
  description?: string
103
+ status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done' | 'awaiting_owner'
104
  priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
105
  project_id?: number
106
  project_ticket_no?: number