diff options
| author | Claudomator Agent <agent@claudomator> | 2026-03-16 01:10:00 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator> | 2026-03-16 01:10:00 +0000 |
| commit | d911021b7e4a0c9f77ca9996b0ebdabb03c56696 (patch) | |
| tree | 9fc5f8ab8bf3497ed25fbae698d7183a9e7c0fbe /web | |
| parent | 7f6254cdafc6143f80ee9ca8e482c36aff2c197e (diff) | |
feat: add elaboration_input field to tasks for richer subtask placeholder
- Add ElaborationInput field to Task struct (task.go)
- Add DB migration and update CREATE/SELECT/scan in storage/db.go
- Update handleCreateTask to accept elaboration_input from API
- Update renderSubtaskRollup in app.js to prefer elaboration_input over description
- Capture elaborate prompt in createTask() form submission
- Update subtask-placeholder tests to cover elaboration_input priority
- Fix missing io import in gemini.go
When a task card is waiting for subtasks, it now shows:
1. The raw user prompt from elaboration (if stored)
2. The task description truncated at word boundary (~120 chars)
3. The task name as fallback
4. 'Waiting for subtasks…' only when all fields are empty
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'web')
| -rw-r--r-- | web/app.js | 5 | ||||
| -rw-r--r-- | web/test/subtask-placeholder.test.mjs | 24 |
2 files changed, 23 insertions, 6 deletions
@@ -883,7 +883,7 @@ async function renderSubtaskRollup(task, footer) { const res = await fetch(`${API_BASE}/api/tasks/${task.id}/subtasks`); const subtasks = await res.json(); if (!subtasks || subtasks.length === 0) { - const blurb = task.description || task.name; + const blurb = task.elaboration_input || task.description || task.name; container.textContent = blurb ? truncateToWordBoundary(blurb) : 'Waiting for subtasks…'; return; } @@ -1555,9 +1555,12 @@ async function createTask(formData) { const workingDir = selectVal === '__new__' ? document.getElementById('new-project-input').value.trim() : selectVal; + const elaboratePromptEl = document.getElementById('elaborate-prompt'); + const elaborationInput = elaboratePromptEl ? elaboratePromptEl.value.trim() : ''; const body = { name: formData.get('name'), description: '', + elaboration_input: elaborationInput || undefined, agent: { instructions: formData.get('instructions'), project_dir: workingDir, diff --git a/web/test/subtask-placeholder.test.mjs b/web/test/subtask-placeholder.test.mjs index 2449faa..b279804 100644 --- a/web/test/subtask-placeholder.test.mjs +++ b/web/test/subtask-placeholder.test.mjs @@ -9,9 +9,10 @@ import assert from 'node:assert/strict'; // // When a task is BLOCKED/READY and the subtask list is empty, the rollup shows // meaningful content instead of a generic placeholder: -// 1. task.description truncated to ~120 chars (word boundary) -// 2. fallback to task.name if no description -// 3. fallback to 'Waiting for subtasks…' if neither +// 1. task.elaboration_input (raw user prompt) if present +// 2. task.description truncated to ~120 chars (word boundary) +// 3. fallback to task.name if no description +// 4. fallback to 'Waiting for subtasks…' if neither function truncateToWordBoundary(text, maxLen = 120) { if (!text || text.length <= maxLen) return text; @@ -20,7 +21,7 @@ function truncateToWordBoundary(text, maxLen = 120) { } function getSubtaskPlaceholder(task) { - const blurb = task.description || task.name; + const blurb = task.elaboration_input || task.description || task.name; return blurb ? truncateToWordBoundary(blurb) : 'Waiting for subtasks…'; } @@ -60,7 +61,20 @@ describe('truncateToWordBoundary', () => { }); describe('getSubtaskPlaceholder', () => { - it('uses task.description when available', () => { + it('prefers task.elaboration_input over description and name', () => { + const task = { elaboration_input: 'fix the login bug', description: 'Fix authentication issue', name: 'auth-fix' }; + assert.equal(getSubtaskPlaceholder(task), 'fix the login bug'); + }); + + it('truncates task.elaboration_input at 120 chars', () => { + const longInput = 'Please fix the login bug that causes users to be logged out unexpectedly when they navigate between pages in the application user interface'; + const task = { elaboration_input: longInput, name: 'auth-fix' }; + const result = getSubtaskPlaceholder(task); + assert.ok(result.endsWith('…'), 'should end with ellipsis'); + assert.ok(result.length <= 122, `result too long: ${result.length}`); + }); + + it('uses task.description when elaboration_input is absent', () => { const task = { description: 'Fix auth bug', name: 'auth-fix' }; assert.equal(getSubtaskPlaceholder(task), 'Fix auth bug'); }); |
