Plan Schema¶
Reference for the plan.json file structure.
Overview¶
A plan.json file contains:
meta: Project metadata
summary: Calculated progress statistics
phases: Array of phases, each containing tasks
decisions: Pending and resolved decisions (optional)
success_metrics: Target metrics (optional)
blockers: Active blockers (optional)
Minimal Example¶
{
"meta": {
"project": "My Project",
"version": "1.0.0",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"business_plan_path": "docs/plan.md"
},
"summary": {
"total_phases": 1,
"total_tasks": 1,
"completed_tasks": 0,
"overall_progress": 0
},
"phases": [
{
"id": "0",
"name": "Setup",
"description": "Initial setup",
"status": "pending",
"progress": { "completed": 0, "total": 1, "percentage": 0 },
"tasks": [
{
"id": "0.1.1",
"title": "Initialize project",
"status": "pending",
"agent_type": null,
"depends_on": [],
"tracking": {}
}
]
}
]
}
JSON Schema Reference¶
The canonical schema that pv validate uses:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://shortlist.app/plan.schema.json",
"title": "Implementation Plan",
"description": "Schema for AI agent-consumable implementation plans",
"type": "object",
"required": ["meta", "summary", "phases"],
"properties": {
"meta": {
"type": "object",
"required": ["project", "version", "created_at", "updated_at", "business_plan_path"],
"properties": {
"project": {"type": "string"},
"version": {"type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$"},
"schema_version": {
"description": "Schema version for migration support (e.g., '1.0.0')",
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$"
},
"created_at": {"type": "string", "format": "date-time"},
"updated_at": {"type": "string", "format": "date-time"},
"business_plan_path": {"type": "string"}
}
},
"summary": {
"type": "object",
"required": ["total_phases", "total_tasks", "completed_tasks", "overall_progress"],
"properties": {
"total_phases": {"type": "integer", "minimum": 0},
"total_tasks": {"type": "integer", "minimum": 0},
"completed_tasks": {"type": "integer", "minimum": 0},
"in_progress_tasks": {"type": "integer", "minimum": 0},
"blocked_tasks": {"type": "integer", "minimum": 0},
"overall_progress": {"type": "number", "minimum": 0, "maximum": 100}
}
},
"phases": {
"type": "array",
"items": {"$ref": "#/definitions/phase"}
},
"decisions": {
"type": "object",
"properties": {
"pending": {
"type": "array",
"items": {"$ref": "#/definitions/decision"}
},
"resolved": {
"type": "array",
"items": {"$ref": "#/definitions/decision"}
}
}
},
"success_metrics": {
"type": "object",
"properties": {
"business_plan_ref": {"$ref": "#/definitions/business_plan_ref"},
"targets": {
"type": "array",
"items": {
"type": "object",
"required": ["metric", "target"],
"properties": {
"metric": {"type": "string"},
"target": {"type": "string"},
"current": {"type": ["string", "null"]}
}
}
}
}
},
"blockers": {
"type": "array",
"items": {"$ref": "#/definitions/blocker"}
}
},
"definitions": {
"status": {
"type": "string",
"enum": ["pending", "in_progress", "completed", "blocked", "skipped"]
},
"agent_type": {
"description": "Agent type identifier. Use kebab-case (e.g., 'python-backend-engineer') or any descriptive name.",
"oneOf": [
{"type": "null"},
{"type": "string", "minLength": 1}
]
},
"skill": {
"description": "Optional skill to invoke for this task (e.g., 'frontend-design'). Skills provide specialized capabilities.",
"oneOf": [
{"type": "null"},
{"type": "string", "minLength": 1}
]
},
"business_plan_ref": {
"type": ["object", "null"],
"properties": {
"section": {"type": "string"},
"lines": {
"type": "array",
"items": {"type": "integer"},
"minItems": 2,
"maxItems": 2
},
"key_content": {"type": "string"}
},
"required": ["section", "lines"]
},
"tracking": {
"type": "object",
"properties": {
"started_at": {"type": ["string", "null"], "format": "date-time"},
"completed_at": {"type": ["string", "null"], "format": "date-time"},
"time_spent_minutes": {"type": ["integer", "null"], "minimum": 0},
"notes": {"type": ["string", "null"]},
"attempts": {"type": "integer", "minimum": 0, "default": 0},
"last_error": {"type": ["string", "null"]},
"assigned_agent_id": {"type": ["string", "null"]}
}
},
"subtask": {
"type": "object",
"required": ["id", "title", "status"],
"properties": {
"id": {"type": "string"},
"title": {"type": "string"},
"status": {"$ref": "#/definitions/status"}
}
},
"priority": {
"type": ["string", "null"],
"enum": [null, "low", "medium", "high"]
},
"estimated_minutes": {
"type": ["integer", "null"],
"minimum": 0
},
"task": {
"type": "object",
"required": ["id", "title", "status", "agent_type", "depends_on", "tracking"],
"properties": {
"id": {"type": "string", "pattern": "^[a-z0-9]+\\.\\d+\\.\\d+$", "description": "Task ID format: phase.section.task (e.g., '0.1.1' or 'bugs.1.1')"},
"title": {"type": "string"},
"status": {"$ref": "#/definitions/status"},
"priority": {"$ref": "#/definitions/priority"},
"estimated_minutes": {"$ref": "#/definitions/estimated_minutes"},
"agent_type": {"$ref": "#/definitions/agent_type"},
"skill": {"$ref": "#/definitions/skill"},
"business_plan_ref": {"$ref": "#/definitions/business_plan_ref"},
"depends_on": {
"description": "Task IDs this task depends on (must be completed first)",
"type": "array",
"items": {"type": "string", "pattern": "^[a-z0-9]+\\.\\d+\\.\\d+$"},
"uniqueItems": true
},
"subtasks": {
"type": "array",
"items": {"$ref": "#/definitions/subtask"}
},
"tracking": {"$ref": "#/definitions/tracking"}
}
},
"phase": {
"type": "object",
"required": ["id", "name", "description", "status", "progress", "tasks"],
"properties": {
"id": {"type": "string"},
"name": {"type": "string"},
"description": {"type": "string"},
"status": {"$ref": "#/definitions/status"},
"progress": {
"type": "object",
"required": ["completed", "total", "percentage"],
"properties": {
"completed": {"type": "integer", "minimum": 0},
"total": {"type": "integer", "minimum": 0},
"percentage": {"type": "number", "minimum": 0, "maximum": 100}
}
},
"tasks": {
"type": "array",
"items": {"$ref": "#/definitions/task"}
}
}
},
"decision": {
"type": "object",
"required": ["id", "question", "options"],
"properties": {
"id": {"type": "string"},
"question": {"type": "string"},
"options": {
"type": "array",
"items": {"type": ["string", "array"]}
},
"recommended": {"type": ["string", "array", "null"]},
"decided": {"type": ["string", "array", "null"]},
"decided_at": {"type": ["string", "null"], "format": "date-time"},
"decided_by": {"type": ["string", "null"]},
"business_plan_ref": {"$ref": "#/definitions/business_plan_ref"}
}
},
"blocker": {
"type": "object",
"required": ["id", "description", "affects_tasks", "created_at"],
"properties": {
"id": {"type": "string"},
"description": {"type": "string"},
"affects_tasks": {
"type": "array",
"items": {"type": "string"}
},
"created_at": {"type": "string", "format": "date-time"},
"resolved_at": {"type": ["string", "null"], "format": "date-time"},
"resolution": {"type": ["string", "null"]}
}
}
}
}
Quick Reference¶
Meta Object¶
Field |
Type |
Required |
Description |
|---|---|---|---|
|
string |
Yes |
Project name |
|
string |
Yes |
Plan version (semver: |
|
string |
No |
Schema version for migrations |
|
string |
Yes |
ISO 8601 creation timestamp |
|
string |
Yes |
ISO 8601 last update timestamp |
|
string |
Yes |
Path to business plan document |
Summary Object¶
Automatically calculated by pv when saving:
Field |
Type |
Description |
|---|---|---|
|
integer |
Number of phases |
|
integer |
Total tasks across all phases |
|
integer |
Number of completed tasks |
|
integer |
Currently active tasks |
|
integer |
Blocked tasks |
|
number |
Percentage complete (0-100) |
Phase Object¶
Field |
Type |
Required |
Description |
|---|---|---|---|
|
string |
Yes |
Unique phase identifier |
|
string |
Yes |
Phase name |
|
string |
Yes |
Phase description |
|
string |
Yes |
pending, in_progress, completed, blocked, skipped |
|
object |
Yes |
|
|
array |
Yes |
Array of task objects |
Special Phase IDs¶
deferred: Tasks postponed for later consideration99: Bug tracking phase (convention)
Task Object¶
Field |
Type |
Required |
Description |
|---|---|---|---|
|
string |
Yes |
Task ID format: |
|
string |
Yes |
Task title |
|
string |
Yes |
pending, in_progress, completed, blocked, skipped |
|
string/null |
Yes |
Agent type or null |
|
array |
Yes |
Task IDs this depends on |
|
object |
Yes |
Timestamps and metadata |
|
string/null |
No |
null, low, medium, high |
|
integer/null |
No |
Estimated time |
|
array |
No |
Nested subtask objects |
|
object/null |
No |
Reference to business plan section |
Tracking Object¶
Field |
Type |
Description |
|---|---|---|
|
string/null |
ISO 8601 start timestamp |
|
string/null |
ISO 8601 completion timestamp |
|
integer/null |
Actual time spent |
|
string/null |
Free-form notes |
|
integer |
Number of attempts (default: 0) |
|
string/null |
Last error message |
|
string/null |
Agent instance ID |
Valid Statuses¶
Status |
Meaning |
|---|---|
|
Not started |
|
Currently being worked on |
|
Finished successfully |
|
Cannot proceed |
|
Intentionally skipped |
Priority Values¶
Priority |
Meaning |
|---|---|
|
No priority set (default) |
|
Low priority |
|
Medium priority |
|
High priority |
Task ID Format¶
Task IDs follow the pattern phase.section.task:
0.1.1 -> Phase 0, section 1, task 1
1.2.3 -> Phase 1, section 2, task 3
99.1.5 -> Bugs phase, section 1, task 5
Auto-Calculated Fields¶
When saving a plan (via any edit command), pv automatically:
Updates
meta.updated_atto current timestampRecalculates
progressfor each phaseRecalculates the
summarytotalsUpdates phase
statusbased on task completion
Validation¶
Use pv validate to check your plan.json against the schema:
$ pv validate
plan.json is valid
$ pv validate --json
{"valid": true, "path": "plan.json"}
Invalid plans show the specific error:
$ pv validate
Validation failed for plan.json:
'status' is a required property
Path: phases.0.tasks.0
Agent Types¶
Agent types are flexible strings. Common conventions:
Type |
Use Case |
|---|---|
|
Default, general tasks |
|
Python backend development |
|
Frontend/UI development |
|
Git operations, PR management |
|
Documentation tasks |
|
Architecture decisions |
|
Testing tasks |
Any non-empty string is valid, or null for unassigned.
Example Files¶
The repository includes example plans:
examples/simple.json: Basic project with 3 phases, 5 tasksexamples/complex.json: Full-featured example with priorities, estimates, decisions
Explore them:
pv -f examples/simple.json
pv -f examples/complex.json summary