Add chorus-entrypoint label to standardized label set
**Problem**: The standardized label set was missing the `chorus-entrypoint` label, which is present in CHORUS repository and required for triggering council formation for project kickoffs. **Changes**: - Added `chorus-entrypoint` label (#ff6b6b) to `EnsureRequiredLabels()` in `internal/gitea/client.go` - Now creates 9 standard labels (was 8): 1. bug 2. bzzz-task 3. chorus-entrypoint (NEW) 4. duplicate 5. enhancement 6. help wanted 7. invalid 8. question 9. wontfix **Testing**: - Rebuilt and deployed WHOOSH with updated label configuration - Synced labels to all 5 monitored repositories (whoosh-ui, SequentialThinkingForCHORUS, TEST, WHOOSH, CHORUS) - Verified all repositories now have complete 9-label set **Impact**: All CHORUS ecosystem repositories now have consistent labeling matching the CHORUS repository standard, enabling proper council formation triggers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
426
docs/TASK-UI-ISSUES-ANALYSIS.md
Normal file
426
docs/TASK-UI-ISSUES-ANALYSIS.md
Normal file
@@ -0,0 +1,426 @@
|
||||
# Task UI Issues Analysis
|
||||
|
||||
## Problem Statement
|
||||
Tasks displayed in the WHOOSH UI show "undefined" fields and placeholder text like "Help Promises: (Not implemented)" and "Retry Budgets: (Not implemented)".
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
### 1. UI Displaying Non-Existent Fields
|
||||
|
||||
**Location**: `ui/script.js` lines ~290-310 (loadTaskDetail function)
|
||||
|
||||
**Current Code**:
|
||||
```javascript
|
||||
async function loadTaskDetail(taskId) {
|
||||
const task = await apiFetch(`/v1/tasks/${taskId}`);
|
||||
taskContent.innerHTML = `
|
||||
<h2>${task.title}</h2>
|
||||
<div class="card">
|
||||
<h3>Task Details</h3>
|
||||
<div class="grid">
|
||||
<div>
|
||||
<p><strong>Status:</strong> ${task.status}</p>
|
||||
<p><strong>Priority:</strong> ${task.priority}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p><strong>Help Promises:</strong> (Not implemented)</p>
|
||||
<p><strong>Retry Budgets:</strong> (Not implemented)</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<p><strong>Description:</strong></p>
|
||||
<p>${task.description}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
**Issue**: "Help Promises" and "Retry Budgets" are hard-coded placeholder text, not actual fields from the Task model.
|
||||
|
||||
### 2. Missing Task Fields in UI
|
||||
|
||||
**Task Model** (`internal/tasks/models.go`):
|
||||
```go
|
||||
type Task struct {
|
||||
ID uuid.UUID
|
||||
ExternalID string
|
||||
ExternalURL string
|
||||
SourceType SourceType
|
||||
Title string
|
||||
Description string
|
||||
Status TaskStatus
|
||||
Priority TaskPriority
|
||||
AssignedTeamID *uuid.UUID
|
||||
AssignedAgentID *uuid.UUID
|
||||
Repository string
|
||||
ProjectID string
|
||||
Labels []string
|
||||
TechStack []string
|
||||
Requirements []string
|
||||
EstimatedHours int
|
||||
ComplexityScore float64
|
||||
ClaimedAt *time.Time
|
||||
StartedAt *time.Time
|
||||
CompletedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
```
|
||||
|
||||
**Fields NOT displayed in UI**:
|
||||
- ❌ Repository
|
||||
- ❌ ProjectID
|
||||
- ❌ Labels
|
||||
- ❌ TechStack
|
||||
- ❌ Requirements
|
||||
- ❌ EstimatedHours
|
||||
- ❌ ComplexityScore
|
||||
- ❌ ExternalURL (link to GITEA issue)
|
||||
- ❌ AssignedTeamID/AssignedAgentID
|
||||
- ❌ Timestamps (claimed_at, started_at, completed_at)
|
||||
|
||||
### 3. API Endpoint Issues
|
||||
|
||||
**Expected Endpoint**: `/api/v1/tasks`
|
||||
**Actual Status**: Returns 404
|
||||
|
||||
**Possible Causes**:
|
||||
1. **Route Registration**: The route exists in code but may not be in the deployed image
|
||||
2. **Image Version**: Running image `anthonyrawlins/whoosh:council-team-fix` may pre-date the `/v1/tasks` endpoint
|
||||
3. **Alternative Access Pattern**: Tasks may need to be accessed via `/api/v1/projects/{projectID}/tasks`
|
||||
|
||||
**Evidence from code**:
|
||||
- `internal/server/server.go` shows both endpoints exist:
|
||||
- `/api/v1/tasks` (standalone tasks endpoint)
|
||||
- `/api/v1/projects/{projectID}/tasks` (project-scoped tasks)
|
||||
|
||||
### 4. Undefined Field Values
|
||||
|
||||
When the UI attempts to display task fields that don't exist in the API response, JavaScript will show `undefined`.
|
||||
|
||||
**Example Scenario**:
|
||||
```javascript
|
||||
// If API returns task without 'estimated_hours'
|
||||
<p><strong>Estimated Hours:</strong> ${task.estimated_hours}</p>
|
||||
// Renders as: "Estimated Hours: undefined"
|
||||
```
|
||||
|
||||
## Impact Assessment
|
||||
|
||||
### Current State
|
||||
1. ✅ Task model in database has all necessary fields
|
||||
2. ✅ Task service can query and return complete task data
|
||||
3. ❌ UI only displays: title, status, priority, description
|
||||
4. ❌ UI shows placeholder text for non-existent fields
|
||||
5. ❌ Many useful task fields are not displayed
|
||||
6. ❓ `/v1/tasks` API endpoint returns 404 (needs verification)
|
||||
|
||||
### User Impact
|
||||
- **Low Information Density**: Users can't see repository, labels, tech stack, requirements
|
||||
- **No Assignment Visibility**: Can't see which team/agent claimed the task
|
||||
- **No Time Tracking**: Can't see when task was claimed/started/completed
|
||||
- **Confusing Placeholders**: "(Not implemented)" text suggests incomplete features
|
||||
- **No External Links**: Can't click through to GITEA issue
|
||||
|
||||
## Recommended Fixes
|
||||
|
||||
### Phase 1: Fix UI Display (HIGH PRIORITY)
|
||||
|
||||
**1.1 Remove Placeholder Text**
|
||||
|
||||
```javascript
|
||||
// REMOVE these lines from loadTaskDetail():
|
||||
<p><strong>Help Promises:</strong> (Not implemented)</p>
|
||||
<p><strong>Retry Budgets:</strong> (Not implemented)</p>
|
||||
```
|
||||
|
||||
**1.2 Add Missing Fields**
|
||||
|
||||
```javascript
|
||||
async function loadTaskDetail(taskId) {
|
||||
const task = await apiFetch(`/v1/tasks/${taskId}`);
|
||||
taskContent.innerHTML = `
|
||||
<h2>${task.title}</h2>
|
||||
|
||||
<div class="card">
|
||||
<h3>Task Details</h3>
|
||||
|
||||
<!-- Basic Info -->
|
||||
<div class="grid">
|
||||
<div>
|
||||
<p><strong>Status:</strong> <span class="badge status-${task.status}">${task.status}</span></p>
|
||||
<p><strong>Priority:</strong> <span class="badge priority-${task.priority}">${task.priority}</span></p>
|
||||
<p><strong>Source:</strong> ${task.source_type}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p><strong>Repository:</strong> ${task.repository || 'N/A'}</p>
|
||||
<p><strong>Project ID:</strong> ${task.project_id || 'N/A'}</p>
|
||||
${task.external_url ? `<p><strong>Issue:</strong> <a href="${task.external_url}" target="_blank">View on GITEA</a></p>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Estimation & Complexity -->
|
||||
${task.estimated_hours || task.complexity_score ? `
|
||||
<hr>
|
||||
<div class="grid">
|
||||
${task.estimated_hours ? `<p><strong>Estimated Hours:</strong> ${task.estimated_hours}</p>` : ''}
|
||||
${task.complexity_score ? `<p><strong>Complexity Score:</strong> ${task.complexity_score.toFixed(2)}</p>` : ''}
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!-- Labels & Tech Stack -->
|
||||
${task.labels?.length || task.tech_stack?.length ? `
|
||||
<hr>
|
||||
<div class="grid">
|
||||
${task.labels?.length ? `
|
||||
<div>
|
||||
<p><strong>Labels:</strong></p>
|
||||
<div class="tags">
|
||||
${task.labels.map(label => `<span class="tag">${label}</span>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
${task.tech_stack?.length ? `
|
||||
<div>
|
||||
<p><strong>Tech Stack:</strong></p>
|
||||
<div class="tags">
|
||||
${task.tech_stack.map(tech => `<span class="tag tech">${tech}</span>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!-- Requirements -->
|
||||
${task.requirements?.length ? `
|
||||
<hr>
|
||||
<p><strong>Requirements:</strong></p>
|
||||
<ul>
|
||||
${task.requirements.map(req => `<li>${req}</li>`).join('')}
|
||||
</ul>
|
||||
` : ''}
|
||||
|
||||
<!-- Description -->
|
||||
<hr>
|
||||
<p><strong>Description:</strong></p>
|
||||
<div class="description">
|
||||
${task.description || '<em>No description provided</em>'}
|
||||
</div>
|
||||
|
||||
<!-- Assignment Info -->
|
||||
${task.assigned_team_id || task.assigned_agent_id ? `
|
||||
<hr>
|
||||
<p><strong>Assignment:</strong></p>
|
||||
<div class="grid">
|
||||
${task.assigned_team_id ? `<p>Team: ${task.assigned_team_id}</p>` : ''}
|
||||
${task.assigned_agent_id ? `<p>Agent: ${task.assigned_agent_id}</p>` : ''}
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!-- Timestamps -->
|
||||
<hr>
|
||||
<div class="grid timestamps">
|
||||
<p><strong>Created:</strong> ${new Date(task.created_at).toLocaleString()}</p>
|
||||
${task.claimed_at ? `<p><strong>Claimed:</strong> ${new Date(task.claimed_at).toLocaleString()}</p>` : ''}
|
||||
${task.started_at ? `<p><strong>Started:</strong> ${new Date(task.started_at).toLocaleString()}</p>` : ''}
|
||||
${task.completed_at ? `<p><strong>Completed:</strong> ${new Date(task.completed_at).toLocaleString()}</p>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
**1.3 Add Corresponding CSS**
|
||||
|
||||
Add to `ui/styles.css`:
|
||||
```css
|
||||
.badge {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-open { background-color: #3b82f6; color: white; }
|
||||
.status-claimed { background-color: #8b5cf6; color: white; }
|
||||
.status-in_progress { background-color: #f59e0b; color: white; }
|
||||
.status-completed { background-color: #10b981; color: white; }
|
||||
.status-closed { background-color: #6b7280; color: white; }
|
||||
.status-blocked { background-color: #ef4444; color: white; }
|
||||
|
||||
.priority-critical { background-color: #dc2626; color: white; }
|
||||
.priority-high { background-color: #f59e0b; color: white; }
|
||||
.priority-medium { background-color: #3b82f6; color: white; }
|
||||
.priority-low { background-color: #6b7280; color: white; }
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 0.25rem 0.75rem;
|
||||
background-color: #e5e7eb;
|
||||
border-radius: 12px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.tag.tech {
|
||||
background-color: #dbeafe;
|
||||
color: #1e40af;
|
||||
}
|
||||
|
||||
.description {
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.6;
|
||||
padding: 1rem;
|
||||
background-color: #f9fafb;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.timestamps {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Verify API Endpoint (MEDIUM PRIORITY)
|
||||
|
||||
**2.1 Test Current Endpoint**
|
||||
```bash
|
||||
# Check if /v1/tasks works
|
||||
curl -v http://whoosh.chorus.services/api/v1/tasks
|
||||
|
||||
# If 404, try project-scoped endpoint
|
||||
curl http://whoosh.chorus.services/api/v1/projects | jq '.projects[0].id'
|
||||
# Then
|
||||
curl http://whoosh.chorus.services/api/v1/projects/{PROJECT_ID}/tasks
|
||||
```
|
||||
|
||||
**2.2 Update UI Route If Needed**
|
||||
|
||||
If `/v1/tasks` doesn't exist in deployed version, update UI to use project-scoped endpoint:
|
||||
|
||||
```javascript
|
||||
// Option A: Load from specific project
|
||||
const task = await apiFetch(`/v1/projects/${projectId}/tasks/${taskNumber}`);
|
||||
|
||||
// Option B: Rebuild and deploy WHOOSH with /v1/tasks endpoint
|
||||
```
|
||||
|
||||
### Phase 3: Task List Enhancement (LOW PRIORITY)
|
||||
|
||||
**3.1 Improve Task List Display**
|
||||
|
||||
```javascript
|
||||
async function loadTasks() {
|
||||
const tasksContent = document.getElementById('tasks-content');
|
||||
try {
|
||||
const data = await apiFetch('/v1/tasks');
|
||||
tasksContent.innerHTML = `
|
||||
<div class="task-list">
|
||||
${data.tasks.map(task => `
|
||||
<div class="task-card">
|
||||
<h3><a href="#tasks/${task.id}">${task.title}</a></h3>
|
||||
<div class="task-meta">
|
||||
<span class="badge status-${task.status}">${task.status}</span>
|
||||
<span class="badge priority-${task.priority}">${task.priority}</span>
|
||||
${task.repository ? `<span class="repo-badge">${task.repository}</span>` : ''}
|
||||
</div>
|
||||
${task.tech_stack?.length ? `
|
||||
<div class="tags">
|
||||
${task.tech_stack.slice(0, 3).map(tech => `
|
||||
<span class="tag tech">${tech}</span>
|
||||
`).join('')}
|
||||
${task.tech_stack.length > 3 ? `<span class="tag">+${task.tech_stack.length - 3} more</span>` : ''}
|
||||
</div>
|
||||
` : ''}
|
||||
${task.description ? `
|
||||
<p class="task-description">${task.description.substring(0, 150)}...</p>
|
||||
` : ''}
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
} catch (error) {
|
||||
tasksContent.innerHTML = `<p class="error">Error loading tasks: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Step 1: Quick Win - Remove Placeholders (5 minutes)
|
||||
1. Open `ui/script.js`
|
||||
2. Find `loadTaskDetail` function
|
||||
3. Remove lines with "Help Promises" and "Retry Budgets"
|
||||
4. Commit and deploy
|
||||
|
||||
### Step 2: Add Essential Fields (30 minutes)
|
||||
1. Add repository, project_id, external_url to task detail view
|
||||
2. Add labels and tech_stack display
|
||||
3. Add timestamps display
|
||||
4. Test locally
|
||||
|
||||
### Step 3: Add Styling (15 minutes)
|
||||
1. Add badge styles for status/priority
|
||||
2. Add tag styles for labels/tech stack
|
||||
3. Add description formatting
|
||||
4. Test visual appearance
|
||||
|
||||
### Step 4: Deploy (10 minutes)
|
||||
1. Build new WHOOSH image with UI changes
|
||||
2. Tag as `anthonyrawlins/whoosh:task-ui-fix`
|
||||
3. Deploy to swarm
|
||||
4. Verify in browser
|
||||
|
||||
### Step 5: API Verification (Optional)
|
||||
1. Test if `/v1/tasks` endpoint works after deploy
|
||||
2. If not, rebuild WHOOSH binary with latest code
|
||||
3. Or update UI to use project-scoped endpoints
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Task detail page loads without "undefined" values
|
||||
- [ ] No placeholder "(Not implemented)" text visible
|
||||
- [ ] Repository name displays correctly
|
||||
- [ ] Labels render as styled tags
|
||||
- [ ] Tech stack renders as styled tags
|
||||
- [ ] External URL link works and opens GITEA issue
|
||||
- [ ] Timestamps format correctly
|
||||
- [ ] Status badge has correct color
|
||||
- [ ] Priority badge has correct color
|
||||
- [ ] Description text wraps properly
|
||||
- [ ] Null/empty fields don't break layout
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
1. **Interactive Task Management**
|
||||
- Claim task button
|
||||
- Update status dropdown
|
||||
- Add comment/note functionality
|
||||
|
||||
2. **Task Filtering**
|
||||
- Filter by status, priority, repository
|
||||
- Search by title/description
|
||||
- Filter by tech stack
|
||||
|
||||
3. **Task Analytics**
|
||||
- Time to completion metrics
|
||||
- Complexity vs actual hours
|
||||
- Agent performance by task type
|
||||
|
||||
4. **Visualization**
|
||||
- Kanban board view
|
||||
- Timeline view
|
||||
- Dependency graph
|
||||
|
||||
## References
|
||||
|
||||
- Task Model: `internal/tasks/models.go`
|
||||
- Task Service: `internal/tasks/service.go`
|
||||
- UI JavaScript: `ui/script.js`
|
||||
- UI Styles: `ui/styles.css`
|
||||
- API Routes: `internal/server/server.go`
|
||||
Reference in New Issue
Block a user