Break down freeform text (feature specs, meeting notes, bug reports) into structured tusk tasks with deduplication
Takes arbitrary text input — feature specs, meeting notes, brainstorm lists, bug reports, requirements docs — and decomposes it into structured, deduplicated tasks in the tusk database.
The user provides freeform text after /create-task. This could be:
If the user didn't provide any text after the command, ask:
What would you like to turn into tasks? Paste any text — feature specs, meeting notes, bug reports, requirements, etc.
Check whether deferred insertion was requested before proceeding:
--deferred/create-task --deferred <text>If either condition is met, set deferred mode = on and strip the --deferred flag (if present) from the input text before proceeding. Do not ask the user to confirm deferred mode — it was explicitly requested.
If neither condition is met, deferred mode = off and all tasks are inserted as active (existing behavior, no change).
Fetch everything needed for analysis in a single call:
tusk setup
This returns a JSON object with two keys:
config — full project config (domains, task_types, agents, priorities, complexity, etc.). Store for use when assigning metadata. If a field is an empty list (e.g., "domains": []), that field has no validation — use your best judgment or leave it NULL.backlog — all open tasks as an array of objects. Hold in context for Step 3. The heuristic dupe checker (tusk dupes check) catches textually similar tasks, but you can catch semantic duplicates that differ in wording — e.g., "Implement password reset flow" vs. existing "Add forgot password endpoint" — which the heuristic would miss.Break the input into discrete, actionable tasks. For each task, determine:
| Field | How to Determine |
|---|---|
| summary | Clear, imperative sentence describing the deliverable (e.g., "Add login endpoint with JWT authentication"). Max ~100 chars. |
| description | Expanded context from the input — acceptance criteria, technical notes, relevant quotes from the source text. |
| priority | Infer from language cues: "critical"/"urgent"/"blocking" → Highest/High; "nice to have"/"eventually" → Low/Lowest; default to Medium. Must be one of the configured priorities. |
| domain | Match to a configured domain based on the task's subject area. Leave NULL if no domains are configured or none fit. |
| task_type | Categorize as one of the configured task types (bug, feature, refactor, test, docs, infrastructure). Default to feature for new work, bug for fixes. For test and docs: use as task_type only when writing tests or docs is the primary deliverable — otherwise use acceptance criteria. See Task Type Decision Guide below. |
| assignee | Match to a configured agent if the task clearly falls in their area. Leave NULL if unsure. |
| complexity | Estimate effort: XS = partial session, S = 1 session, M = 2-3 sessions, L = 3-5 sessions, XL = 5+. Default to M if unclear. Must be one of the configured complexity values. |
The key question: Is this type the primary deliverable, or is it proof that another deliverable is done?
| Task Type | Use as task_type when the work is this | Use as acceptance criterion when this verifies other work |
|---|---|---|
| bug | The deliverable is fixing a defect — "Fix login crash on empty password" | A regression must not recur — "Empty password no longer crashes" |
| feature | The deliverable is new functionality | N/A — features are always tasks, never criteria |
| refactor | The deliverable is restructuring code without changing behavior | N/A — refactoring is always a primary deliverable, never just verification |
| test | Writing tests is the goal — "Write test suite for auth module" | Tests verify a feature is done — "All auth endpoints have passing tests" |
| docs | Writing docs is the goal — "Write v2→v3 migration guide" | Docs confirm completion — "API endpoint is documented in README" |
| infrastructure | The deliverable is tooling, CI, or infra changes | N/A — infra work is always a task |
Key rule: If removing the work would leave the feature itself incomplete → use as task_type. If removing it just removes verification of an already-complete feature → use as an acceptance criterion.
Run this step only when the input describes a bug that claims a specific test is failing or references a pre-existing test failure. Skip for all other input types.
Trigger signals (any one is sufficient):
task_type determined in Step 3 is bug and the description references a test by name or pathIf triggered:
Detect the test command:
tusk test-detect
If confidence is "none", skip the rest of this step — no test runner could be identified.
Run the referenced test. Extract the test name, file, or pattern from the input and run it against the detected command. For example, if the test command is pytest and the input mentions test_foo_bar, run:
<test_command> <test_reference> # e.g. pytest tests/unit/test_foo.py::test_foo_bar
If no specific test name or file can be identified from the input, skip the rest of this step — treat as indeterminate. Limit to 60 seconds. If the command times out or errors for reasons unrelated to test failure (e.g. import error, missing dependency), skip the rest of this step — treat as indeterminate.
Evaluate the result:
Test fails (non-zero exit): Failure confirmed. Proceed to Step 4 without comment — the bug is real.
Test passes (exit 0): Surface this before presenting the proposal:
Pre-verification note: The referenced test is currently passing on this branch — the failure described may not be pre-existing. Do you still want to create a bug task for it?
Wait for the user's response. If they say no or cancel, stop. If they confirm, proceed to Step 4 with the original task fields unchanged.
If analysis produced exactly 1 task, use the compact inline format instead of the full table:
## Proposed Task
**Add login endpoint with JWT auth** (High · api · feature · M · backend)
> Implement POST /auth/login that validates credentials and returns a JWT token. Include refresh token support.
Then ask:
Create this task? You can confirm, edit (e.g., "change priority to Medium"), or remove it.
If analysis produced 2 or more tasks, show the full numbered table:
## Proposed Tasks
| # | Summary | Priority | Domain | Type | Complexity | Assignee |
|---|---------|----------|--------|------|------------|----------|
| 1 | Add login endpoint with JWT auth | High | api | feature | M | backend |
| 2 | Add signup page with form validation | Medium | frontend | feature | S | frontend |
### Details
**1. Add login endpoint with JWT auth**
> Implement POST /auth/login that validates credentials and returns a JWT token. Include refresh token support.
**2. Add signup page with form validation**
> Create signup form with email, password, and confirm password fields. Validate on blur and on submit.
Then ask:
Does this look right? You can:
- Confirm to create all tasks
- Remove specific numbers (e.g., "remove 3")
- Edit a task (e.g., "change 2 priority to High")
- Add a task you think is missing
If deferred mode = on, add a notice directly below the task list (before asking for confirmation):
Note: deferred mode is on — all tasks will be inserted with
--deferred(60-day expiry,[Deferred]prefix).
This lets the user opt out (e.g., by editing or cancelling) before insertion.
Wait for explicit user approval before proceeding. Do NOT insert anything until the user confirms.
For each approved task, generate 3–7 acceptance criteria — concrete, testable conditions that define "done." Derive them from the description: each distinct requirement or expected behavior maps to a criterion. For bug tasks, include a criterion that the failure case is resolved. For feature tasks, include the happy path and at least one edge case. For any task that creates a new database table (or is in a schema-related domain), always include the criterion: "DOMAIN.md updated with schema entry for <table_name>".
Before inserting, apply these rules to every generated criterion:
Prohibited patterns — Never generate a criterion whose text contains any of the following. These commands run against the live environment and can destroy data or corrupt history:
tusk init --force — wipes the live task databasegit reset --hard — discards uncommitted workgit push --force / git push -f — overwrites remote historyrm -rf — recursive deletionDROP TABLE / DROP DATABASE — destructive SQLInit verification redirect — If the task involves verifying tusk init behavior (e.g., "init creates the schema correctly", "init recreates the DB under --force"), do not generate a criterion that runs tusk init against the live database. Instead, use the integration test suite, which spins up a temporary DB automatically:
python3 -m pytest tests/integration/ -k test_init -qpasses
Warning on detection — If any generated criterion matches a prohibited pattern, display a warning and stop before inserting:
⚠️ Dangerous criterion detected: The proposed criterion
"<criterion text>"contains a destructive command (<pattern>). This would run against the live database and could cause data loss. Replace it with a safer alternative (e.g., an integration test assertion) before inserting.
Revise the criterion and present it to the user for approval before proceeding to insertion.
Then insert the task with criteria in a single call using tusk task-insert. This validates enum values against config, runs a heuristic duplicate check internally, and inserts the task + criteria in one transaction:
tusk task-insert "<summary>" "<description>" \
--priority "<priority>" \
--domain "<domain>" \
--task-type "<task_type>" \
--assignee "<assignee>" \
--complexity "<complexity>" \
--criteria "<criterion 1>" \
--criteria "<criterion 2>" \
--criteria "<criterion 3>"
When deferred mode = on, append --deferred to every tusk task-insert call. This flag applies uniformly to all tasks in the batch — it cannot be set per-task mid-flow:
tusk task-insert "<summary>" "<description>" \
--priority "<priority>" \
--domain "<domain>" \
--task-type "<task_type>" \
--assignee "<assignee>" \
--complexity "<complexity>" \
--criteria "<criterion 1>" \
--criteria "<criterion 2>" \
--criteria "<criterion 3>" \
--deferred
For typed criteria with automated verification, use --typed-criteria with a JSON object:
tusk task-insert "<summary>" "<description>" \
--criteria "Manual criterion" \
--typed-criteria '{"text":"Tests pass","type":"test","spec":"pytest tests/"}' \
--typed-criteria '{"text":"Config exists","type":"file","spec":"config/*.json"}'
Valid types: manual (default), code, test, file. Non-manual types require a spec field.
Omit --domain or --assignee entirely if the value is NULL/empty — do not pass empty strings.
The command prints JSON with task_id and criteria_ids. Use the task_id for dependency proposals in Step 7.
The command prints JSON with matched_task_id and similarity. Report which existing task matched:
Skipped "Add login endpoint with JWT auth" — duplicate of existing task #12 (similarity 0.87)
Report the error and skip.
Skip this step if:
If two or more tasks were created, analyze for dependencies. Load the dependency proposal guide:
Read file: <base_directory>/DEPENDENCIES.md
Then follow its instructions.
After processing all tasks, show a summary:
## Results
**Created**: 3 tasks (#14, #15, #16)
**Skipped**: 1 duplicate (matched existing #12)
**Dependencies added**: 2 (#16 → #14 (blocks), #17 → #14 (contingent))
| ID | Summary | Priority | Domain |
|----|---------|----------|--------|
| 14 | Add signup page with form validation | Medium | frontend |
| 15 | Fix broken CSS on mobile nav | High | frontend |
| 16 | Add rate limiting middleware | Medium | api |
When deferred mode = on, label the created line as **Created (deferred)** instead of **Created**:
**Created (deferred)**: 3 tasks (#14, #15, #16)
Include the Dependencies added line only when Step 7 was executed (i.e., two or more tasks were created). If Step 7 was skipped (all duplicates, single-task fast path, or user skipped all dependencies), omit the line. If dependencies were proposed but the user removed some, only list the ones actually inserted.
After displaying the summary, verify that every created task has at least one acceptance criterion. For each created task ID, run:
tusk criteria list <task_id>
If any task has zero criteria, display a warning:
Warning: Tasks #14, #16 have no acceptance criteria. Go back to Step 6 and generate criteria for them before moving on.
Do not proceed past this step until all created tasks have at least one criterion.
Then, conditionally show the updated backlog:
If more than 3 tasks were created, show the full backlog so the user can see where the new tasks landed:
tusk -header -column "SELECT id, summary, priority, domain, task_type, assignee FROM tasks WHERE status = 'To Do' ORDER BY priority_score DESC, id"
If 3 or fewer tasks were created, show only a count to save tokens:
tusk "SELECT COUNT(*) || ' open tasks in backlog' FROM tasks WHERE status = 'To Do'"