feat: add awaiting_owner task status detection (#428)

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
This commit is contained in:
nyk 2026-03-17 13:52:37 +07:00 committed by GitHub
parent d0cd8701c7
commit fc4384bba9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 38 additions and 4 deletions

View File

@ -593,6 +593,7 @@
"columnAriaLabel": "عمود {title}، {count} مهمة",
"colInbox": "البريد الوارد",
"colAssigned": "مُخصّص",
"colAwaitingOwner": "بانتظار المالك",
"colInProgress": "قيد التنفيذ",
"colReview": "المراجعة",
"colQualityReview": "مراجعة الجودة",

View File

@ -593,6 +593,7 @@
"columnAriaLabel": "Spalte {title}, {count} Aufgaben",
"colInbox": "Posteingang",
"colAssigned": "Zugewiesen",
"colAwaitingOwner": "Wartet auf Besitzer",
"colInProgress": "In Bearbeitung",
"colReview": "Überprüfung",
"colQualityReview": "Qualitätsprüfung",

View File

@ -742,6 +742,7 @@
"columnAriaLabel": "{title} column, {count} tasks",
"colInbox": "Inbox",
"colAssigned": "Assigned",
"colAwaitingOwner": "Awaiting Owner",
"colInProgress": "In Progress",
"colReview": "Review",
"colQualityReview": "Quality Review",

View File

@ -593,6 +593,7 @@
"columnAriaLabel": "Columna {title}, {count} tareas",
"colInbox": "Bandeja de entrada",
"colAssigned": "Asignado",
"colAwaitingOwner": "Esperando al propietario",
"colInProgress": "En progreso",
"colReview": "Revisión",
"colQualityReview": "Revisión de calidad",

View File

@ -593,6 +593,7 @@
"columnAriaLabel": "Colonne {title}, {count} tâches",
"colInbox": "Boîte de réception",
"colAssigned": "Assigné",
"colAwaitingOwner": "En attente du propriétaire",
"colInProgress": "En cours",
"colReview": "Révision",
"colQualityReview": "Révision qualité",

View File

@ -593,6 +593,7 @@
"columnAriaLabel": "{title}列、{count}件のタスク",
"colInbox": "受信トレイ",
"colAssigned": "割り当て済み",
"colAwaitingOwner": "オーナー待ち",
"colInProgress": "進行中",
"colReview": "レビュー",
"colQualityReview": "品質レビュー",

View File

@ -593,6 +593,7 @@
"columnAriaLabel": "{title} 열, {count}개 작업",
"colInbox": "받은 편지함",
"colAssigned": "할당됨",
"colAwaitingOwner": "소유자 대기 중",
"colInProgress": "진행 중",
"colReview": "검토",
"colQualityReview": "품질 검토",

View File

@ -593,6 +593,7 @@
"columnAriaLabel": "Coluna {title}, {count} tarefas",
"colInbox": "Caixa de entrada",
"colAssigned": "Atribuído",
"colAwaitingOwner": "Aguardando proprietário",
"colInProgress": "Em andamento",
"colReview": "Revisão",
"colQualityReview": "Revisão de qualidade",

View File

@ -593,6 +593,7 @@
"columnAriaLabel": "Колонка {title}, {count} задач",
"colInbox": "Входящие",
"colAssigned": "Назначено",
"colAwaitingOwner": "Ожидает владельца",
"colInProgress": "В работе",
"colReview": "На проверке",
"colQualityReview": "Контроль качества",

View File

@ -831,6 +831,7 @@
"columnAriaLabel": "{title} 列,{count} 个任务",
"colInbox": "收件箱",
"colAssigned": "已分配",
"colAwaitingOwner": "等待所有者",
"colInProgress": "进行中",
"colReview": "审查",
"colQualityReview": "质量审查",

View File

@ -22,7 +22,7 @@ interface Task {
id: number
title: string
description?: string
status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done'
status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done' | 'awaiting_owner'
priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
assigned_to?: string
created_by: string
@ -89,12 +89,27 @@ interface MentionOption {
const STATUS_COLUMN_KEYS = [
{ key: 'inbox', titleKey: 'colInbox', color: 'bg-secondary text-foreground' },
{ key: 'assigned', titleKey: 'colAssigned', color: 'bg-blue-500/20 text-blue-400' },
{ key: 'awaiting_owner', titleKey: 'colAwaitingOwner', color: 'bg-orange-500/20 text-orange-400' },
{ key: 'in_progress', titleKey: 'colInProgress', color: 'bg-yellow-500/20 text-yellow-400' },
{ key: 'review', titleKey: 'colReview', color: 'bg-purple-500/20 text-purple-400' },
{ key: 'quality_review', titleKey: 'colQualityReview', color: 'bg-indigo-500/20 text-indigo-400' },
{ key: 'done', titleKey: 'colDone', color: 'bg-green-500/20 text-green-400' },
]
const AWAITING_OWNER_KEYWORDS = [
'waiting for', 'waiting on', 'needs human', 'manual action',
'account creation', 'browser login', 'approval needed',
'owner action', 'human required', 'blocked on owner',
'awaiting owner', 'awaiting human', 'needs owner',
]
function detectAwaitingOwner(task: Task): boolean {
if (task.status === 'awaiting_owner') return true
if (task.status !== 'assigned' && task.status !== 'in_progress') return false
const text = `${task.title} ${task.description || ''}`.toLowerCase()
return AWAITING_OWNER_KEYWORDS.some(kw => text.includes(kw))
}
/** Build a human-readable label for a session key like "agent:nefes:telegram-group-123" */
function formatSessionLabel(s: { key: string; channel?: string; kind?: string; label?: string }): string {
if (s.label) return s.label
@ -535,9 +550,12 @@ export function TaskBoardPanel() {
// Poll as SSE fallback — pauses when SSE is delivering events
useSmartPoll(fetchData, 30000, { pauseWhenSseConnected: true })
// Group tasks by status
// Group tasks by status, overriding for awaiting_owner detection
const tasksByStatus = statusColumns.reduce((acc, column) => {
acc[column.key] = tasks.filter(task => task.status === column.key)
acc[column.key] = tasks.filter(task => {
const effectiveStatus = detectAwaitingOwner(task) ? 'awaiting_owner' : task.status
return effectiveStatus === column.key
})
return acc
}, {} as Record<string, Task[]>)
@ -1017,6 +1035,11 @@ export function TaskBoardPanel() {
Aegis
</span>
)}
{detectAwaitingOwner(task) && (
<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">
{t('colAwaitingOwner')}
</span>
)}
</div>
</div>
</div>
@ -1820,6 +1843,7 @@ function ClaudeCodeTasksSection() {
s === 'completed' ? 'text-green-400' :
s === 'in_progress' ? 'text-blue-400' :
s === 'blocked' ? 'text-red-400' :
s === 'awaiting_owner' ? 'text-orange-400' :
'text-muted-foreground'
return (

View File

@ -100,7 +100,7 @@ export interface Task {
id: number
title: string
description?: string
status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done'
status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done' | 'awaiting_owner'
priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
project_id?: number
project_ticket_no?: number