Verify-gate uitbreiden: DIVERGENT/PARTIAL vereist agent-acknowledgement (#53)
* feat(schema): add Task.verify_required enum (ALIGNED / ALIGNED_OR_PARTIAL / ANY) Adds VerifyRequired enum and verify_required field (default ALIGNED_OR_PARTIAL) to the Task model. Also declares the claude_jobs_status_finished_at_idx index in the schema to match the live DB. Applied via db execute + migrate resolve. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ui): add verify_required select to TaskDetailDialog SoloTask interface, solo page mapping, solo store, PATCH route handler and TaskDetailDialog all updated to expose the three-level verify gate (ALIGNED / ALIGNED_OR_PARTIAL / ANY) as a native select. Disabled with DemoTooltip in demo mode. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d93c91c386
commit
ced0a8a4c0
9 changed files with 87 additions and 8 deletions
|
|
@ -26,6 +26,7 @@ export interface SoloTask {
|
|||
sort_order: number
|
||||
status: 'TO_DO' | 'IN_PROGRESS' | 'REVIEW' | 'DONE'
|
||||
verify_only: boolean
|
||||
verify_required: 'ALIGNED' | 'ALIGNED_OR_PARTIAL' | 'ANY'
|
||||
story_id: string
|
||||
story_code: string | null
|
||||
story_title: string
|
||||
|
|
|
|||
|
|
@ -55,16 +55,24 @@ const VERIFY_RESULT_CONFIG: Record<string, { label: string; className: string }>
|
|||
|
||||
type SaveState = 'idle' | 'saving' | 'saved'
|
||||
|
||||
const VERIFY_REQUIRED_LABELS: Record<string, string> = {
|
||||
ALIGNED: 'Strikt — alleen ALIGNED',
|
||||
ALIGNED_OR_PARTIAL: 'Standaard — ALIGNED of PARTIAL met uitleg',
|
||||
ANY: 'Vrij — geen verify-eis',
|
||||
}
|
||||
|
||||
function TaskDetailContent({ task, productId, isDemo, repoUrl, onClose }: TaskDetailContentProps) {
|
||||
const { updatePlan, updateVerifyOnly } = useSoloStore()
|
||||
const { updatePlan, updateVerifyOnly, updateVerifyRequired } = useSoloStore()
|
||||
const job = useSoloStore(s => s.claudeJobsByTaskId[task.id])
|
||||
const connectedWorkers = useSoloStore(s => s.connectedWorkers)
|
||||
const [localPlan, setLocalPlan] = useState(task.implementation_plan ?? '')
|
||||
const [localVerifyOnly, setLocalVerifyOnly] = useState(task.verify_only)
|
||||
const [localVerifyRequired, setLocalVerifyRequired] = useState(task.verify_required)
|
||||
const [saveState, setSaveState] = useState<SaveState>('idle')
|
||||
const [, startTransition] = useTransition()
|
||||
const [jobPending, startJobTransition] = useTransition()
|
||||
const [verifyOnlyPending, startVerifyOnlyTransition] = useTransition()
|
||||
const [verifyRequiredPending, startVerifyRequiredTransition] = useTransition()
|
||||
const fadeTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const savedPlanRef = useRef(task.implementation_plan ?? '')
|
||||
|
||||
|
|
@ -145,6 +153,32 @@ function TaskDetailContent({ task, productId, isDemo, repoUrl, onClose }: TaskDe
|
|||
})
|
||||
}
|
||||
|
||||
function handleVerifyRequiredChange(e: React.ChangeEvent<HTMLSelectElement>) {
|
||||
if (isDemo) return
|
||||
const newValue = e.target.value as typeof localVerifyRequired
|
||||
const prevValue = localVerifyRequired
|
||||
setLocalVerifyRequired(newValue)
|
||||
startVerifyRequiredTransition(async () => {
|
||||
try {
|
||||
const res = await fetch(`/api/tasks/${task.id}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ verify_required: newValue }),
|
||||
})
|
||||
if (!res.ok) {
|
||||
setLocalVerifyRequired(prevValue)
|
||||
toast.error('Verify-required bijwerken mislukt')
|
||||
return
|
||||
}
|
||||
updateVerifyRequired(task.id, newValue)
|
||||
} catch {
|
||||
setLocalVerifyRequired(prevValue)
|
||||
toast.error('Verify-required bijwerken mislukt')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
|
|
@ -220,6 +254,22 @@ function TaskDetailContent({ task, productId, isDemo, repoUrl, onClose }: TaskDe
|
|||
<span className="text-xs text-muted-foreground">Alleen verifiëren (niet implementeren)</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground shrink-0">Verify-gate:</span>
|
||||
<DemoTooltip show={isDemo}>
|
||||
<select
|
||||
value={localVerifyRequired}
|
||||
onChange={handleVerifyRequiredChange}
|
||||
disabled={isDemo || verifyRequiredPending}
|
||||
className="text-xs rounded-md border border-border bg-surface-container px-2 py-1 text-foreground focus:outline-none focus:ring-1 focus:ring-primary disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{(['ALIGNED', 'ALIGNED_OR_PARTIAL', 'ANY'] as const).map(v => (
|
||||
<option key={v} value={v}>{VERIFY_REQUIRED_LABELS[v]}</option>
|
||||
))}
|
||||
</select>
|
||||
</DemoTooltip>
|
||||
</div>
|
||||
|
||||
<div className="-mx-4 -mb-4 flex flex-wrap items-center gap-2 border-t bg-muted/50 px-4 py-3 rounded-b-xl">
|
||||
<Link
|
||||
href={`/products/${productId}/sprint/planning`}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue