taskval validates task definitions against the Structured Task Template Specification. It performs two-tier validation: structural checks via JSON Schema, then semantic checks for cross-node consistency and content quality.
taskval [flags] <file.json>
taskval [flags] -
| Flag | Type | Default | Values | Description |
|---|---|---|---|---|
--mode |
string | graph |
task, graph |
task: validate a single task node. graph: validate a full task graph with milestones and dependencies. |
--output |
string | text |
text, json |
text: human/LLM-readable formatted output. json: machine-readable structured JSON. |
--create-beads |
bool | false |
On validation success, create Beads issues via the bd CLI. Requires bd on PATH and an initialized beads database (bd init). |
|
--dry-run |
bool | false |
Show the bd commands that would be executed without running them. Requires --create-beads. |
|
--epic-title |
string | "" |
Override the auto-generated epic title (graph mode only). Ignored in single task mode. | |
--help |
Print usage information. |
| Code | Meaning |
|---|---|
0 |
Validation passed. No ERROR-severity findings. Warnings may be present. With --create-beads, issues were created successfully. |
1 |
Validation failed. One or more ERROR-severity findings. |
2 |
Usage error (bad flag, missing file, too many files), internal error (schema compilation failure), or bd command failure (e.g., bd not found, beads not initialized, bd create error). |
taskval accepts exactly one positional argument: a file path or - for stdin.
taskval --mode=task my_task.json
taskval --mode=graph my_graph.jsoncat my_task.json | taskval --mode=task -
echo '{"version":"0.1.0","tasks":[...]}' | taskval -Every example below shows the exact command, the complete output, and the exit code as verified against the examples/ directory in this repository.
$ taskval --helptaskval — Structured Task Template Spec validator
Usage:
taskval [flags] <file.json>
taskval [flags] - (read from stdin)
Flags:
-mode string
Validation mode: 'task' for a single task node, 'graph' for a full task graph (default "graph")
-output string
Output format: 'text' for human/LLM-readable, 'json' for machine-readable (default "text")
Exit codes:
0 Validation passed (no errors)
1 Validation failed (errors found)
2 Usage or internal error
Exit code: 0
$ taskvalError: no input file specified. Use 'taskval <file.json>' or 'taskval -' for stdin
Exit code: 2
$ taskval file1.json file2.jsonError: expected exactly one input file, got 2
Exit code: 2
$ taskval nonexistent.jsonError: reading file 'nonexistent.json': open nonexistent.json: no such file or directory
Exit code: 2
$ taskval --mode=invalid examples/valid_single_task.jsonError: invalid mode 'invalid'. Must be 'task' or 'graph'.
Exit code: 2
$ taskval --output=xml examples/valid_single_task.jsonError: invalid output format 'xml'. Must be 'text' or 'json'.
Exit code: 2
$ echo "not json at all" | taskval --mode=task -VALIDATION FAILED
Summary: 1 error(s), 0 warning(s), 0 info(s) across 0 task(s)
--- ERRORS (must fix) ---
1. [ERROR] Rule SCHEMA
Path: format
Problem: Invalid JSON format
Exit code: 1
$ taskval --mode=task examples/valid_single_task.jsonVALIDATION PASSED
Tasks validated: 1
No errors or warnings.
Exit code: 0
$ taskval --mode=task --output=json examples/valid_single_task.json{
"valid": true,
"stats": {
"total_tasks": 1,
"error_count": 0,
"warning_count": 0,
"info_count": 0
}
}Exit code: 0
$ taskval examples/valid_task_graph.jsonVALIDATION PASSED
Tasks validated: 3
No errors or warnings.
Exit code: 0
Note: --mode=graph is the default and can be omitted.
$ taskval --output=json examples/valid_task_graph.json{
"valid": true,
"stats": {
"total_tasks": 3,
"error_count": 0,
"warning_count": 0,
"info_count": 0
}
}Exit code: 0
Input file examples/invalid_task.json contains: uppercase task_id, task_name over 80 chars, empty inputs/outputs arrays, invalid priority and estimate enum values.
$ taskval --mode=task examples/invalid_task.jsonVALIDATION FAILED
Summary: 9 error(s), 0 warning(s), 0 info(s) across 0 task(s)
--- ERRORS (must fix) ---
1. [ERROR] Rule SCHEMA
Path: properties
Problem: Properties 'estimate', 'inputs', 'outputs', 'priority', 'task_id',
'task_name' do not match their schemas
2. [ERROR] Rule SCHEMA
Path: /estimate/enum
Problem: Value huge should be one of the allowed values: trivial, small,
medium, large, unknown
3. [ERROR] Rule SCHEMA
Path: /task_name/maxLength
Problem: Value should be at most 80 characters
4. [ERROR] Rule SCHEMA
Path: /outputs/minItems
Problem: Value should have at least 1 items
5. [ERROR] Rule SCHEMA
Path: /task_id/pattern
Problem: Value does not match the required pattern ^[a-z0-9]+(-[a-z0-9]+)*$
Fix: Add the missing required field at '/task_id/pattern'. Check the
spec's Quick Reference (Appendix A) for the expected format.
6. [ERROR] Rule SCHEMA
Path: /inputs/minItems
Problem: Value should have at least 1 items
7. [ERROR] Rule SCHEMA
Path: /depends_on/$ref
Problem: Value does not match the reference schema
8. [ERROR] Rule SCHEMA
Path: /depends_on/type
Problem: Value is array but should be object
9. [ERROR] Rule SCHEMA
Path: /priority/enum
Problem: Value urgent should be one of the allowed values: critical, high,
medium, low
Exit code: 1
Tier 2 semantic checks are skipped because Tier 1 failed.
$ taskval --mode=task --output=json examples/invalid_task.json{
"valid": false,
"errors": [
{
"rule": "SCHEMA",
"severity": "ERROR",
"path": "properties",
"message": "Properties 'estimate', 'inputs', 'outputs', 'priority', 'task_id', 'task_name' do not match their schemas"
},
{
"rule": "SCHEMA",
"severity": "ERROR",
"path": "/priority/enum",
"message": "Value urgent should be one of the allowed values: critical, high, medium, low"
},
{
"rule": "SCHEMA",
"severity": "ERROR",
"path": "/task_name/maxLength",
"message": "Value should be at most 80 characters"
},
{
"rule": "SCHEMA",
"severity": "ERROR",
"path": "/inputs/minItems",
"message": "Value should have at least 1 items"
},
{
"rule": "SCHEMA",
"severity": "ERROR",
"path": "/depends_on/$ref",
"message": "Value does not match the reference schema"
},
{
"rule": "SCHEMA",
"severity": "ERROR",
"path": "/depends_on/type",
"message": "Value is array but should be object"
},
{
"rule": "SCHEMA",
"severity": "ERROR",
"path": "/estimate/enum",
"message": "Value huge should be one of the allowed values: trivial, small, medium, large, unknown"
},
{
"rule": "SCHEMA",
"severity": "ERROR",
"path": "/outputs/minItems",
"message": "Value should have at least 1 items"
},
{
"rule": "SCHEMA",
"severity": "ERROR",
"path": "/task_id/pattern",
"message": "Value does not match the required pattern ^[a-z0-9]+(-[a-z0-9]+)*$",
"suggestion": "Add the missing required field at '/task_id/pattern'. Check the spec's Quick Reference (Appendix A) for the expected format."
}
],
"stats": {
"total_tasks": 0,
"error_count": 9,
"warning_count": 0,
"info_count": 0
}
}Exit code: 1
Input file examples/invalid_semantic.json passes Tier 1 but contains: a dependency cycle between task-a and task-b, a dangling reference to nonexistent-task, forbidden words in a goal, vague acceptance criteria, and missing contextual fields.
$ taskval examples/invalid_semantic.jsonVALIDATION FAILED
Summary: 5 error(s), 7 warning(s), 0 info(s) across 3 task(s)
--- ERRORS (must fix) ---
1. [ERROR] Rule V4
Path: tasks[2].depends_on
Problem: Task 'task-c' depends on 'nonexistent-task', but no task with that
task_id exists in the graph.
Fix: Either add a task with task_id 'nonexistent-task' to the graph, or
remove 'nonexistent-task' from the depends_on list of task
'task-c'.
Value: "nonexistent-task"
2. [ERROR] Rule V5
Path: tasks
Problem: Dependency graph contains a cycle. 2 task(s) are involved:
[task-a, task-b]. A valid task graph must be a DAG (Directed
Acyclic Graph).
Fix: Review the depends_on fields of the listed tasks. Break the cycle
by removing one dependency or decomposing a task into sub-tasks.
Value: "task-a, task-b"
3. [ERROR] Rule V6
Path: tasks[0].goal
Problem: Goal contains the forbidden word/phrase 'try'. Goals must describe
testable outcomes, not activities or explorations.
Fix: Rewrite the goal as a concrete, testable outcome. Instead of 'try
...', describe what the system does when the task is complete.
Example: 'The function returns X when given Y.'
Value: "Try to explore adding feature A and investigate options for it"
4. [ERROR] Rule V6
Path: tasks[0].goal
Problem: Goal contains the forbidden word/phrase 'explore'. Goals must
describe testable outcomes, not activities or explorations.
Fix: Rewrite the goal as a concrete, testable outcome. Instead of
'explore ...', describe what the system does when the task is
complete. Example: 'The function returns X when given Y.'
Value: "Try to explore adding feature A and investigate options for it"
5. [ERROR] Rule V6
Path: tasks[0].goal
Problem: Goal contains the forbidden word/phrase 'investigate'. Goals must
describe testable outcomes, not activities or explorations.
Fix: Rewrite the goal as a concrete, testable outcome. Instead of
'investigate ...', describe what the system does when the task is
complete. Example: 'The function returns X when given Y.'
Value: "Try to explore adding feature A and investigate options for it"
--- WARNINGS (should fix) ---
6. [WARNING] Rule V6
Path: tasks[1].goal
Problem: Goal starts with 'To ...' which suggests an activity rather than a
testable outcome.
Fix: Rewrite as a state-of-the-world assertion. Example: Instead of 'To
add search functionality', write 'The Search() function returns
ranked results from Weaviate hybrid search.'
Value: "To add feature B that does something useful"
7. [WARNING] Rule V7
Path: tasks[0].acceptance[0]
Problem: Acceptance criterion contains the vague phrase 'works correctly'.
Criteria must be independently verifiable with concrete expected
values.
Fix: Replace with a specific assertion. Example: Instead of 'it works
correctly', write 'Given input "test", the function returns
["result1", "result2"] with status 200.'
Value: "it works correctly"
8. [WARNING] Rule V7
Path: tasks[0].acceptance[1]
Problem: Acceptance criterion contains the vague phrase 'looks right'.
Criteria must be independently verifiable with concrete expected
values.
Fix: Replace with a specific assertion. Example: Instead of 'it works
correctly', write 'Given input "test", the function returns
["result1", "result2"] with status 200.'
Value: "output looks right and is fine"
9. [WARNING] Rule V7
Path: tasks[0].acceptance[1]
Problem: Acceptance criterion contains the vague phrase 'is fine'. Criteria
must be independently verifiable with concrete expected values.
Fix: Replace with a specific assertion. Example: Instead of 'it works
correctly', write 'Given input "test", the function returns
["result1", "result2"] with status 200.'
Value: "output looks right and is fine"
10. [WARNING] Rule V9
Path: tasks[2].constraints
Problem: Contextual field 'constraints' is missing from task 'task-c'.
Contextual fields should be explicitly present or set to
{"status": "N/A", "reason": "..."}.
Fix: Either provide a value for 'constraints' or explicitly mark it as
not applicable: {"status": "N/A", "reason": "your justification
here"}.
11. [WARNING] Rule V9
Path: tasks[2].files_scope
Problem: Contextual field 'files_scope' is missing from task 'task-c'.
Contextual fields should be explicitly present or set to
{"status": "N/A", "reason": "..."}.
Fix: Either provide a value for 'files_scope' or explicitly mark it as
not applicable: {"status": "N/A", "reason": "your justification
here"}.
12. [WARNING] Rule V10
Path: tasks[2].files_scope
Problem: Task 'task-c' appears to be an implementation task (name starts
with an implementation verb) but has no files_scope defined.
Fix: Add a files_scope listing the files the agent should create or
modify. This prevents unintended changes to other parts of the
codebase.
Exit code: 1
$ taskval --output=json examples/invalid_semantic.json{
"valid": false,
"errors": [
{
"rule": "V4",
"severity": "ERROR",
"path": "tasks[2].depends_on",
"message": "Task 'task-c' depends on 'nonexistent-task', but no task with that task_id exists in the graph.",
"suggestion": "Either add a task with task_id 'nonexistent-task' to the graph, or remove 'nonexistent-task' from the depends_on list of task 'task-c'.",
"context": "nonexistent-task"
},
{
"rule": "V5",
"severity": "ERROR",
"path": "tasks",
"message": "Dependency graph contains a cycle. 2 task(s) are involved: [task-a, task-b]. A valid task graph must be a DAG (Directed Acyclic Graph).",
"suggestion": "Review the depends_on fields of the listed tasks. Break the cycle by removing one dependency or decomposing a task into sub-tasks.",
"context": "task-a, task-b"
},
{
"rule": "V6",
"severity": "ERROR",
"path": "tasks[0].goal",
"message": "Goal contains the forbidden word/phrase 'try'. Goals must describe testable outcomes, not activities or explorations.",
"suggestion": "Rewrite the goal as a concrete, testable outcome. Instead of 'try ...', describe what the system does when the task is complete. Example: 'The function returns X when given Y.'",
"context": "Try to explore adding feature A and investigate options for it"
},
...
],
"stats": {
"total_tasks": 3,
"error_count": 5,
"warning_count": 7,
"info_count": 0
}
}Exit code: 1
$ cat examples/valid_single_task.json | taskval --mode=task -VALIDATION PASSED
Tasks validated: 1
No errors or warnings.
Exit code: 0
Using --mode=graph on a single task file produces schema errors because the document lacks the required version and tasks envelope:
$ taskval --mode=graph examples/valid_single_task.jsonVALIDATION FAILED
Summary: 21 error(s), 0 warning(s), 0 info(s) across 0 task(s)
--- ERRORS (must fix) ---
1. [ERROR] Rule SCHEMA
Path: required
Problem: Required properties 'version', 'tasks' are missing
2. [ERROR] Rule SCHEMA
Path: additionalProperties
Problem: Additional properties 'outputs', 'notes', 'non_goals',
'error_cases', 'priority', 'estimate', 'inputs', 'constraints',
'effects', 'task_id', 'task_name', 'goal', 'acceptance',
'depends_on', 'files_scope' do not match the schema
...
Exit code: 1
The --create-beads flag creates bd issues after successful validation. Use --dry-run to preview commands without executing them.
$ taskval --mode=task --create-beads --dry-run examples/valid_single_task.jsonVALIDATION PASSED
Tasks validated: 1
No errors or warnings.
BEADS CREATION (DRY RUN)
[DRY-RUN] bd create --title "Implement user authentication endpoint" --type task --description "..." --acceptance "- ..." --priority 2 --estimate 240 --labels taskval-managed --silent
Summary: Would create 0 epic + 1 tasks, link 0 dependencies.
Exit code: 0
In single task mode, no epic is created. One bd create command is generated per task, plus a bd update --design command (omitted from dry-run for brevity) that stores machine-readable template metadata.
$ taskval --create-beads --dry-run examples/valid_task_graph.jsonVALIDATION PASSED
Tasks validated: 3
No errors or warnings.
BEADS CREATION (DRY RUN)
[DRY-RUN] bd create --title "Task Graph: valid_task_graph.json" --type epic --priority 2 --labels taskval-managed --silent
[DRY-RUN] bd create --title "..." --type task --description "..." --acceptance "- ..." --priority 2 --estimate 60 --parent <epic-id> --labels taskval-managed --silent
[DRY-RUN] bd create --title "..." --type task ...
[DRY-RUN] bd create --title "..." --type task ...
[DRY-RUN] bd dep add <task-b-id> <task-a-id>
Summary: Would create 1 epic + 3 tasks, link 1 dependencies.
Exit code: 0
In graph mode, an epic is created first, then tasks in topological (dependency) order, then dependency links via bd dep add. Each task is parented to the epic.
$ taskval --create-beads --dry-run --epic-title "Sprint 42: Auth System" examples/valid_task_graph.jsonThe epic title resolution order is:
--epic-titleflag value (if provided)- First milestone name in the graph (prefixed with "Task Graph: ")
- Input filename (prefixed with "Task Graph: ")
"Task Graph: (stdin)"for stdin input
$ taskval --mode=task --create-beads examples/valid_single_task.jsonVALIDATION PASSED
Tasks validated: 1
No errors or warnings.
BEADS CREATION
Task created: proj-t42 "Implement user authentication endpoint" (implement-auth)
Summary: 0 epic + 1 tasks created, 0 dependencies linked.
Exit code: 0
When --create-beads is used without --dry-run, commands are executed against the live bd database. The output shows actual issue IDs assigned by bd.
$ taskval --mode=task --create-beads --output=json examples/valid_single_task.json{
"valid": true,
"stats": {
"total_tasks": 1,
"error_count": 0,
"warning_count": 0,
"info_count": 0
},
"beads": {
"tasks": {
"implement-auth": "proj-t42"
},
"dependencies_linked": 0,
"total_created": 1
}
}Exit code: 0
When --output=json is combined with --create-beads, the JSON output includes a beads object alongside the validation result. The beads object maps template task_id values to the actual bd issue IDs.
$ taskval --dry-run examples/valid_task_graph.jsonError: --dry-run requires --create-beads.
Exit code: 2
Beads issues are only created when validation passes. If validation fails, --create-beads is ignored and the normal validation error output is shown.
$ taskval --mode=task --create-beads examples/invalid_task.jsonVALIDATION FAILED
Summary: 9 error(s), 0 warning(s), 0 info(s) across 0 task(s)
--- ERRORS (must fix) ---
...
Exit code: 1
All reported with rule ID SCHEMA and severity ERROR.
| What | Schema keyword | Example error message |
|---|---|---|
| Missing required field | required |
Required properties 'task_id', 'goal' are missing |
| Wrong type | type |
Value is array but should be object |
| Pattern mismatch | pattern |
Value does not match the required pattern ^[a-z0-9]+(-[a-z0-9]+)*$ |
| Enum mismatch | enum |
Value urgent should be one of the allowed values: critical, high, medium, low |
| String too long | maxLength |
Value should be at most 80 characters |
| Array too short | minItems |
Value should have at least 1 items |
| Unknown field | additionalProperties |
Additional properties 'foo' do not match the schema |
| oneOf mismatch | oneOf / $ref |
Value does not match the reference schema |
| Rule ID | Severity | What it checks |
|---|---|---|
| V2 | ERROR | Every task_id is unique across all tasks in the graph |
| V4 | ERROR | Every ID in depends_on references an existing task |
| V5 | ERROR | No task depends on itself |
| V5 | ERROR | The dependency graph is acyclic (DAG). Uses Kahn's algorithm. |
| V6 | ERROR | goal does not contain: "try", "explore", "investigate", "look into" |
| V6 | WARNING | goal does not start with "To ..." |
| V7 | WARNING | acceptance criteria do not contain: "works correctly", "is correct", "is good", "looks right", "properly", "as expected", "should work", "is fine" |
| V9 | WARNING | Contextual fields (depends_on, constraints, files_scope) are present or explicitly N/A |
| V10 | WARNING | Implementation tasks (name starts with implement/add/fix/create/build/write) have files_scope |
| MILESTONE | ERROR | No duplicate milestone names; all task_ids and depends_on_milestones references resolve |
Validation passed (clean):
VALIDATION PASSED
Tasks validated: <N>
No errors or warnings.
Validation passed (with warnings):
VALIDATION PASSED (with warnings)
Summary: 0 error(s), <W> warning(s), <I> info(s) across <N> task(s)
--- WARNINGS (should fix) ---
1. [WARNING] Rule <ID>
Path: <json.path>
Problem: <description>
Fix: <suggestion>
Value: "<offending value>"
Validation failed:
VALIDATION FAILED
Summary: <E> error(s), <W> warning(s), <I> info(s) across <N> task(s)
--- ERRORS (must fix) ---
1. [ERROR] Rule <ID>
Path: <json.path>
Problem: <description>
Fix: <suggestion>
Value: "<offending value>"
--- WARNINGS (should fix) ---
...
{
"valid": true|false,
"errors": [
{
"rule": "V5",
"severity": "ERROR",
"path": "tasks",
"message": "Dependency graph contains a cycle...",
"suggestion": "Review the depends_on fields...",
"context": "task-a, task-b"
}
],
"stats": {
"total_tasks": 3,
"error_count": 1,
"warning_count": 2,
"info_count": 0
}
}JSON error object fields:
| Field | Type | Always present | Description |
|---|---|---|---|
rule |
string | yes | Rule ID: SCHEMA, V2, V4, V5, V6, V7, V9, V10, MILESTONE |
severity |
string | yes | ERROR, WARNING, or INFO |
path |
string | yes | JSON path to the problematic field, e.g. tasks[0].goal |
message |
string | yes | Human/LLM-readable problem description |
suggestion |
string | no | Actionable fix recommendation (omitted if empty) |
context |
string | no | The offending value, truncated to 120 chars (omitted if empty) |
When --create-beads and --output=json are used together, the JSON output includes a beads object:
{
"valid": true,
"stats": {
"total_tasks": 3,
"error_count": 0,
"warning_count": 0,
"info_count": 0
},
"beads": {
"epic_id": "proj-e07",
"tasks": {
"setup-database": "proj-e07.1",
"implement-api": "proj-e07.2",
"write-tests": "proj-e07.3"
},
"dependencies_linked": 2,
"total_created": 4
}
}Beads JSON object fields:
| Field | Type | Always present | Description |
|---|---|---|---|
epic_id |
string | no | The bd issue ID for the epic (graph mode only; omitted in single task mode). |
tasks |
object | yes | Maps each template task_id to its assigned bd issue ID. |
dependencies_linked |
int | yes | Number of bd dep add links created. |
total_created |
int | yes | Total issues created (epic + tasks). |
Single task mode:
BEADS CREATION
Task created: proj-t42 "Implement auth endpoint" (implement-auth)
Summary: 0 epic + 1 tasks created, 0 dependencies linked.
Graph mode:
BEADS CREATION
Epic created: proj-e07 "Task Graph: my_graph.json"
Task created: proj-e07.1 "Setup database schema" (setup-database)
Task created: proj-e07.2 "Implement REST API" (implement-api)
Task created: proj-e07.3 "Write integration tests" (write-tests)
Dependency: proj-e07.2 blocked-by proj-e07.1
Dependency: proj-e07.3 blocked-by proj-e07.2
Summary: 1 epic + 3 tasks created, 2 dependencies linked.
BEADS CREATION (DRY RUN)
[DRY-RUN] bd create --title "..." --type epic --priority 2 --labels taskval-managed --silent
[DRY-RUN] bd create --title "..." --type task --description "..." --parent <epic-id> ...
[DRY-RUN] bd dep add <task-b-id> <task-a-id>
Summary: Would create 1 epic + 3 tasks, link 1 dependencies.
Dry-run output omits bd update --design commands for brevity. Placeholder IDs like <epic-id> and <task-a-id> show how IDs would be substituted at execution time.
How task template fields map to bd CLI flags:
| Template Field | bd Flag | Notes |
|---|---|---|
task_name |
--title |
Truncated to 500 chars. |
goal + inputs + outputs + constraints + non_goals + error_cases |
--description |
Composed as structured markdown. |
acceptance |
--acceptance |
Formatted as markdown list (- criterion). |
priority |
--priority |
Mapped: critical=0, high=1, medium=2, low=3. |
estimate |
--estimate |
Mapped to minutes: trivial=15, small=60, medium=240, large=480. unknown omitted. |
notes |
--notes |
Passed through if non-empty. |
task_id + files_scope + effects + inputs + outputs |
--design |
Stored as JSON _template metadata for machine consumption. |
| (graph mode) | --parent |
Each task is parented to the epic. |
depends_on |
bd dep add |
One command per dependency link. |