You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The interaction the whole product rests on. If ticking a task costs more than a second, the list stops being maintained and every metric downstream becomes a report on a dataset nobody keeps.
Blocked-by: #3 (the hook does the stamping; these actions only flip status).
Files you own
src/actions/task.actions.ts (new) — action metadata, added to dulyActions in src/actions/index.ts
src/actions/task.handlers.ts (new)
src/actions/register-handlers.ts — add your registerTaskActionHandlers(ql) call inside the existing function
test/task-actions.test.ts (new)
Handlers are runtime code wired through register-handlers.ts, which the config already imports. An action whose handler is unregistered renders, is clickable, and fails at call time — there is no author-time gate for it.
No modal. No confirmation dialog. No required note, attachment or percentage.
Visible when record.status is open or in_progress. CEL predicates reference record.<field> — a bare status evaluates to null and hides the action on every record, and pnpm validate now rejects it.
duly_task_undo — row action, appears on a just-completed row.
Sets status = 'in_progress'; the hook clears completed_at.
Undo replaces confirmation. This is the design: an accidental tick costs one click to reverse, so the tick itself needs no ceremony.
duly_task_skip — row action, secondary.
Sets status = 'skipped' and requires skip_reason. This is the one place a modal is correct, because the object's skip_needs_reason validation will reject the write without it.
Skipping is a legitimate outcome — "the plant was down, there was nothing to return". Forcing that answer to be recorded as done or left open corrupts the data either way.
Also expose complete and skip as bulk actions on the list views: a week's worth of ticks in one gesture is the difference between a Monday-morning habit and a chore.
Do not
add a completion percentage, a required evidence upload, or a "are you sure?" step
make attachments a condition of completing anything — duly_task.enable.files is on so people can attach, never so they must
write completed_at or last_update_at from a handler
Acceptance
completing sends { status: 'done' } and nothing else; the record comes back with completed_at set
undo returns the record to in_progress with completed_at null
skip without a reason is rejected by validation with the object's message
bulk-completing 20 selected tasks issues one write path and leaves all 20 stamped
action predicates are all record.<field>-qualified and pnpm validate passes
The interaction the whole product rests on. If ticking a task costs more than a second, the list stops being maintained and every metric downstream becomes a report on a dataset nobody keeps.
Blocked-by: #3 (the hook does the stamping; these actions only flip
status).Files you own
src/actions/task.actions.ts(new) — action metadata, added todulyActionsinsrc/actions/index.tssrc/actions/task.handlers.ts(new)src/actions/register-handlers.ts— add yourregisterTaskActionHandlers(ql)call inside the existing functiontest/task-actions.test.ts(new)Handlers are runtime code wired through
register-handlers.ts, which the config already imports. An action whose handler is unregistered renders, is clickable, and fails at call time — there is no author-time gate for it.Actions
duly_task_complete— row action, primary.status = 'done'. That is the entire payload.completed_atis the hook's job (Task lifecycle hook — completed_at and last_update_at stamping #3); do not send it.record.statusisopenorin_progress. CEL predicates referencerecord.<field>— a barestatusevaluates tonulland hides the action on every record, andpnpm validatenow rejects it.duly_task_undo— row action, appears on a just-completed row.status = 'in_progress'; the hook clearscompleted_at.duly_task_skip— row action, secondary.status = 'skipped'and requiresskip_reason. This is the one place a modal is correct, because the object'sskip_needs_reasonvalidation will reject the write without it.doneor leftopencorrupts the data either way.Also expose complete and skip as bulk actions on the list views: a week's worth of ticks in one gesture is the difference between a Monday-morning habit and a chore.
Do not
duly_task.enable.filesis on so people can attach, never so they mustcompleted_atorlast_update_atfrom a handlerAcceptance
{ status: 'done' }and nothing else; the record comes back withcompleted_atsetin_progresswithcompleted_atnullrecord.<field>-qualified andpnpm validatepassesGates
pnpm validate && pnpm typecheck && pnpm test && pnpm build.