Plan DSL reference
PlanQ DSL v1 is a UTF-8, EDN-shaped data language with one root map. It is not standard EDN and is never evaluated.
Syntax
| Form | Purpose |
|---|---|
{ ... } | Map for a Plan, work item, assignment, or resource object |
[ ... ] | Ordered vector |
:field | Field name |
plain-value | Unquoted scalar when unambiguous |
"quoted value" | Escaped single-line scalar |
| Backtick block | Multiline :description only |
// comment | Line comment; omitted from IR |
@item-id | Explicit work-item or person reference |
Commas are optional separators and canonical formatting removes them. Parenthesized expressions, sets, reader macros, tagged literals, and interpolation are unsupported.
IDs, names, and dates
IDs match [a-z][a-z0-9]*(?:-[a-z0-9]+)* and are unique in their namespace. Plan, task, and milestone IDs are global within an entry include closure. Person keys use a separate project-resource namespace.
Dates are absolute ISO calendar dates in YYYY-MM-DD form. PlanQ never stores relative expressions such as today or +3d.
Names are non-empty single-line values. Quote names containing whitespace, structural punctuation, escapes, or //.
Plan and work-item fields
| Node | Required | Optional |
|---|---|---|
| Plan root | none | :id, :version, :title, :description, :items |
| Leaf task | :task | :id, :description, :start, :end, :deadline, :after, :to |
| Summary task | :task, :items | :id, :description, :start, :end, :deadline, :after, :to |
| Milestone | :milestone | :id, :description, :date, :state, :after |
| Include | :include | :after, :deadline |
| Assignment map | :person, :load | none |
The current DSL version is 1.0. Missing root :id, :version, and :title may be derived from a valid source path and are materialized by formatting. Unknown and duplicate fields are errors.
Scheduling rules
- A leaf task supplies both
:startand:end, or neither. - Both dates are inclusive; equal dates represent one day.
:deadlineis an inclusive hard deadline.- A milestone is a zero-duration event with optional
:date. - Summary dates roll up from terminal descendants.
:afteris an ordered vector of explicit@idfinish-to-start dependencies.- A successor task starts after a predecessor finishes; a successor milestone may share the predecessor's finish date.
PlanQ validates constraints but does not automatically schedule work.
Includes
An include path is relative to the including file, uses /, ends in lowercase .plan, and remains inside the source root. The entry closure is limited to:
| Limit | Value |
|---|---|
| Include depth | 32 |
| Files per entry | 256 |
| Bytes per file | 2 MiB |
| Bytes per closure | 32 MiB |
Included files may reference their own descendants, but cannot reference an includer, a sibling branch, or an entity outside the closure.
Resources
Ordinary .plan files cannot declare calendars or people. Those belong in project-root resources.plan, whose closed field set is:
| Node | Required | Optional |
|---|---|---|
| Resource document | :version | :calendar, :people |
| Calendar | none | :workdays, :holidays |
| Person | :name | :id, :role, :leave, :overtime |
| Leave range | :start, :end | none |
| Overtime map | :date | :capacity |
Assignments reference a stable @person-key. Multiple assignments may use {:person @alice :load 0.5}. Each load and capacity is greater than zero and at most 1.0.
Formatting and versions
planq format operates on source syntax, not generated IR. It uses two-space indentation, stable field order, LF line endings, minimal safe quoting, and a single final newline. Reformatting canonical input is byte-idempotent.
DSL, IR, diagnostics, CLI envelope, and Bridge protocol versions evolve independently. Consumers must read each explicit version rather than infer one from another.
See Authoring Plans and Project Files.