Adds a clientId targeting mode and a dryRun flag to the admin generate-and-assign tool, plus a confirmation dialog showing summary counts before any tasks are actually created. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.6 KiB
Task Generation: Per-Client Mode & Preview-Before-Generate — Design
Status: Approved. Ready for implementation planning.
Problem
The admin "Generate & Assign Tasks" tool (/api/tasks/generate-and-assign,
surfaced on the Tasks → Generate & Assign page) only targets clients by
advocate + designation, which can silently sweep in every client that
advocate/designation pair matches — as discovered this session, that's up to
23 clients for a single pair. There's no way to generate tasks for just one
specific client. There's also no confirmation step anywhere in this flow:
clicking the button immediately writes tasks to the database with no
preview of what will be created or a chance to back out.
Goal: add a per-client targeting mode, and require a preview-and-confirm step before any tasks are actually created, for this admin tool.
Scope
In scope: the admin "Generate & Assign Tasks" page/endpoint only — both its existing advocate+designation mode and the new per-client mode.
Explicitly out of scope (decided during brainstorming): the Policy Group Manager's per-group "Generate Tasks" button, the Setup Wizard's silent auto-generation on client-setup completion, and the silent auto-regeneration that fires when a policy is removed from a group. None of these get a preview step in this phase.
Decisions
| Question | Decision |
|---|---|
| Preview scope | Admin Generate & Assign page only (existing + new per-client mode) |
| Per-client assignee | Always that client's own claimsAdvocateId — not an arbitrary admin pick |
| Selection mode | Separate mode from advocate+designation (toggle), not a combined filter |
| Preview detail level | Summary counts (total + per-client breakdown), not a full task-by-task list, for both modes |
| Preview mechanism | Extend the existing endpoint with a dryRun flag rather than a separate preview endpoint or client-side estimate |
Why dryRun on the existing endpoint, not a separate preview endpoint:
a separate endpoint would duplicate the client/group/policy/template-matching
logic (including the past-due-date filter added earlier this session) —
real risk of the preview drifting out of sync with what actually gets
created, which is exactly the class of bug this session spent most of its
time fixing. A dryRun flag on the same code path guarantees the preview
can never lie about what will happen.
Design
API — POST /api/tasks/generate-and-assign
Body accepts either targeting mode, plus an optional flag:
{ advocateId, designationId, dryRun? } // existing mode
{ clientId, dryRun? } // new mode
Server validates exactly one mode is provided (400 if both or neither).
Client mode specifics:
- Assignee is always the target client's own
claimsAdvocateId. - If that client has no claims advocate set, return an error
(
"Client has no claims advocate assigned") rather than creating unassigned tasks.
Refactor: the route's three sections (renewal groups / ungrouped
policies / client-level templates) already build a tasksToCreate array
before writing (including the isRelevantDueDate past-due filter). Split
each section into "compute what would be created" and "write it," so
dryRun: true runs the exact same computation but skips the
createMany/create calls and the audit log entry (nothing happened, so
nothing to log). A real run (dryRun false/absent) behaves as today, plus
records which mode triggered it (advocate-designation vs client) in the
audit log's newValues.
Dry-run response shape:
{
"dryRun": true,
"clientsFound": 3,
"totalEstimatedTasks": 42,
"advocateName": "Jane Smith",
"clients": [
{ "id": "cl_...", "name": "Acme Corp", "estimatedTasks": 14 }
]
}
UI — "Generate & Assign Tasks" card (src/app/(dashboard)/tasks/assign/page-client.tsx)
- Add a mode toggle: By Advocate + Designation (today's UI, unchanged)
vs By Client (new — a searchable picker using the
clientsprop already passed into this component). In client mode, if the selected client has noclaimsAdvocateId, disable the action and show a helper message explaining why. - The generate button no longer calls the endpoint for real directly. It
first calls with
dryRun: true:totalEstimatedTasks === 0→ just toast the existing "no new tasks" message, no dialog (nothing meaningful to confirm).- Otherwise → open an
AlertDialog(this codebase's existing pattern for confirm-before-consequential-action) showing total counts + the per-client breakdown table, with Confirm (re-calls the endpoint withoutdryRun) and Cancel (closes, no side effects — nothing was written during the preview call).
Testing plan
Route-level tests for generate-and-assign:
dryRun: trueand a real run produce identical counts for the same input (no drift between preview and reality).clientIdmode only ever touches that one client.- Missing-advocate error case for client mode.
- Mutual-exclusivity validation (400 when both or neither targeting mode is provided).
Out of scope / explicitly deferred
- Preview/confirm for the per-group manual button, the Setup Wizard's auto-generation, and the remove-from-group auto-regeneration — all still fire immediately with no confirmation, unchanged by this phase.
- The separately-discovered gap where policies that stay inside a group
never get
POLICY-level tasks generated by any automatic path, and thegenerate-policy-tasksroute's missing idempotency check — both noted during this session's review but not addressed by this design.