Task File Format
Depth:
File Structure
Tasks are markdown files with YAML frontmatter in the aitasks/ directory. Task files use the naming convention t<number>_<name>.md. Executed task files are stored in aitasks/archived/ and their associated plan files in aiplans/archived/.
The number in the filename is the task’s identity, so the convention is load-bearing rather than cosmetic: it is how a listing row, a board card, a lock, an archive entry and a dependency reference all resolve back to the file. ait create always assigns one. A file written into aitasks/ by hand can end up without it, and such a file is unaddressable: ait ls skips it and warns rather than listing a row nothing can act on, and a name further from the convention is not listed at all. Either way the file is invisible to the tooling, so the fix is to rename it to the convention.
---
priority: high
effort: medium
depends: []
issue_type: feature # See aitasks/metadata/task_types.txt for valid types
status: Ready
labels: [ui, backend]
created_at: 2026-01-15 10:00
updated_at: 2026-01-15 10:00
---
## Task description here
Detailed description of what needs to be done.
Frontmatter Fields
| Field | Values | Description |
|---|---|---|
priority | high, medium, low | Task priority for sorting |
effort | low, medium, high | Estimated implementation effort |
depends | [1, 4] | List of task numbers this depends on |
xdeprepo | project name | The one linked project this task coordinates with, as a logical name from the registry. Set with ait create --xdeprepo / ait update --xdeprepo. Setting it alone declares an intent to coordinate without blocking, and is what opts the task into paired cross-repo planning. See Cross-project dependencies |
xdeps | [42, 16_2] | Blocking dependencies on tasks in xdeprepo, written the way that project numbers them locally. Set with --xdeps on either command. Requires xdeprepo — xdeps alone is rejected at create and update time, because task IDs are meaningless without a project to resolve them against |
issue_type | bug, chore, documentation, enhancement, feature, manual_verification, performance, refactor, style, test | Type of work (from task_types.txt) |
status | Ready, Editing, Implementing, Postponed, Done, Folded | Current status |
labels | [ui, backend] | Categorization labels |
created_at | YYYY-MM-DD HH:MM | Creation timestamp |
updated_at | YYYY-MM-DD HH:MM | Last modification timestamp |
completed_at | YYYY-MM-DD HH:MM | Completion timestamp (set on archival) |
archived_reason | superseded | Why the task was archived for a reason other than completion. Written only by ait archive --superseded; absent ⇒ archived as completed. Distinct from folding — a folded task is merged into its primary (see Folded tasks), not superseded |
assigned_to | email address | Developer working on the task |
issue | URL | Linked GitHub/GitLab/Bitbucket issue |
children_to_implement | [t10_1, t10_2] | Remaining child tasks (parent tasks only) |
boardcol | column ID | Board UI column placement |
boardidx | integer | Board UI sort index within column |
boardgroup | slug ([a-z0-9_]+), or "" | Board UI group membership within a column. Set with ait update --boardgroup (update-only — there is no creation flag). An invalid slug is rejected, not coerced, because the slug is the group’s identity. "" is an explicit “ungrouped” tombstone, distinct from the field being absent. See Board groups. |
folded_tasks | [138, 129_5] | Task IDs folded into this task by /aitask-explore or /aitask-fold (deleted on archival) |
folded_into | task number | Task this was folded into (set by /aitask-fold or /aitask-explore) |
anchor | task id (130, 130_2) | Topic-group key pointing at a subject’s root task, so loosely-related and follow-up tasks cluster together. Absent ⇒ the task is its own root. See Topic anchoring. |
followup_kind | manual_verification, risk_mitigation, upstream_defect, verification_failure, carry_over, qa_test_gap, review_finding, docs_gap | Marks the task as an auto-spawned follow-up and records which seam spawned it. Absent ⇒ genuine new work. Orthogonal to issue_type, which stays the task’s real type (an upstream defect is still a bug). Clearing it removes the key — there is no empty-value form. manual_verification additionally requires issue_type: manual_verification. |
file_references | [path, path:N, path:N-M, path:N-M^N-M] | Structured pointers to source files / line ranges. 1-indexed, inclusive. Exact-string dedup. See Creating Tasks from Code |
artifacts | list of mappings | Nested. Versioned, handle-addressed artifacts attached to the task. Each entry carries handle (art:<id>), kind, and an optional name — nothing else: the current version, the version history and the backend live in the artifact manifest, so a new version never rewrites the task file. Written by ait artifact create / rm, never by hand — and because those commands commit the whole task file, they refuse to run while it has uncommitted changes, so commit or revert your edits first. See the nested-field example |
attachments | list of mappings | Nested. Content-addressed file attachments. Each entry carries hash, name, mime, size, added_at and backend. Written by ait attach add / rm, never by hand — and because those commands commit the whole task file, they refuse to run while it has uncommitted changes, so commit or revert your edits first. See the nested-field example |
verifies | [t10_1, t10_2] | Task IDs this task verifies (used by manual_verification sibling tasks that gate release on human-checked behavior) |
verification_baseline | <sha> @ YYYY-MM-DD HH:MM | The commit a manual_verification checklist is known to match. Set with ait update --verification-baseline (update-only). Together with a curated file_references list it enables the advisory staleness pre-check, which warns when a named file changed since this commit; absent ⇒ the check silently skips. Inherited (not reset) by a deferred-item carry-over task |
plan_approved_at | YYYY-MM-DD HH:MM | Marks a task whose plan was approved and whose implementation was deliberately deferred (the “Approve and stop here” checkpoint option), so it is distinguishable from a never-planned task. Written and cleared by the workflow only — it is cleared when implementation starts, on a replan or abort, when the task is decomposed into children, and when a remote-drift stop demands re-verification; absent ⇒ no such plan. Shown by ait ls -v as Plan: approved <ts> and listable with ait ls --plan-approved. A display and prompting signal only: it never changes how the workflow routes, and the post-plan remote drift check runs regardless |
risk_code_health | high, medium, low | Code-health risk (stability, quality, maintainability, blast-radius) assigned by the risk-evaluation planning step; display-only, omitted unless evaluated |
risk_goal_achievement | high, medium, low | Goal-achievement risk (whether the plan will deliver the requested goals) assigned by the risk-evaluation planning step; display-only |
risk_mitigation_tasks | [884_4, 890] | Task IDs created to mitigate risks identified during planning (dropped on fold) |
implemented_with | <agent>/<model> | Agent and model that implemented the task (e.g., claudecode/opus4_7_1m). See Agent attribution |
pull_request | URL | Linked pull request URL (mirrors issue) |
contributor | name | External contributor credited on the commit (used by PR-import flow) |
contributor_email | Email for the contributor’s Co-Authored-By trailer | |
gates | [risk_evaluated, tests_pass] | Declared gate set — the task’s gating intent. Registered gates live in aitasks/metadata/gates.yaml. An explicit [] is an opt-out (never backfilled from a profile) |
also_blocks_dependents | [merge_approved] | Extra gates that must pass before this task’s dependents unblock, beyond the registry’s blocks_dependents defaults. Set with --also-blocks-dependents on both ait create and ait update |
active_gates | [risk_evaluated] | Framework-derived — never hand-edit. The profile-filtered enforced gate set, materialized at pick/claim time (resolve(gates, profile default_gates) ∩ profile rendered set). May be [] (fully filtered). Written atomically with the three fields below |
active_gates_filtered | [docs_updated] | Framework-derived. Gates the profile ceiling removed (declared but not enforced) |
active_gates_profile | profile name | Framework-derived. Provenance stamp: the execution profile that produced the active set |
active_gates_digest | <g>.<p>.<o> | Framework-derived. Three-part integrity digest over the resolve inputs and stored outputs; on mismatch (e.g. a later gates: edit) enforcement falls back to the raw gates: field until the next pick re-materializes |
Nested fields: artifacts and attachments
Every other field above is a flat scalar or a flat list. These two are lists of mappings, and they are the only place the header nests:
artifacts:
- handle: art:trail-shadow-review-loop
kind: implementation_trail
name: "Shadow review-loop automation: landing order"
attachments:
- hash: sha256:1f8b034f74a1…
name: crash-log.txt
mime: text/plain
size: 4821
added_at: 2026-08-14 09:12
backend: local
Neither is hand-edited. artifacts entries are written by
ait artifact and
attachments entries by ait attach; both commands manage the blob, the
manifest and the task file as one commit, so editing the block by hand
desynchronises it from the store.
Fields the workflow writes for you
Some rows above are marked Framework-derived or “written by the workflow
only” — active_gates and its three companions, risk_code_health,
risk_goal_achievement, risk_mitigation_tasks, and plan_approved_at. The
ait update flags that write them exist but are deliberately not documented:
they are internal surfaces the workflow calls at defined points, and setting one
by hand produces a value the next pick recomputes or clears. Where a field is
meant to be set by a person, the flag is named in its own row — see
verification_baseline and boardgroup.
Topic Anchoring
The optional anchor field groups related tasks around a topic root without
making them children of that root. A root task omits the field; its own task id
is its topic key. A related task stores the root id in bare form:
anchor: 130
Anchors are written by aitask_create.sh --anchor <id> or derived with
--followup-of <source_id>, which always resolves to the source task’s root
rather than chaining follow-ups together. Children created with --parent
inherit the parent’s root, so a topic can include a parent, its child tasks, and
loose follow-ups in one board lane.
Use aitask_update.sh --batch <id> --anchor <root> to move a task to a topic,
or --anchor "" to clear the field and make the task its own root again.
Status Workflow
Ready → Editing → Implementing → Done → Archived
↘ Folded (merged into another task)
- Ready — Task is defined and available for implementation
- Editing — Task is being refined (description, requirements)
- Implementing — Active development in progress (assigned to someone)
- Postponed — Deferred for later
- Folded — Task was merged into another task via
/aitask-foldor/aitask-explore; deleted on archival of the primary task - Done — Implementation complete, pending archival
- Archived — Task and plan files moved to
archived/directories
Parent-Child Hierarchies
Tasks support parent-child hierarchies for breaking complex work into subtasks:
- Parent tasks live in
aitasks/(e.g.,aitasks/t10_implement_auth.md) - Child tasks live in
aitasks/t<parent>/(e.g.,aitasks/t10/t10_1_add_login.md) - Child plans live in
aiplans/p<parent>/(e.g.,aiplans/p10/p10_1_add_login.md)
Parent tasks track remaining children via the children_to_implement frontmatter field. When all children are complete, the parent is automatically archived.
Child task IDs use the format t<parent>_<child>_<name>.md where both parent and child identifiers are numbers only. Do not insert tasks “in-between” (e.g., no t10_1b). If you discover a missing step, add it as the next available number and adjust dependencies.
Archive Storage
Completed tasks move through the archive lifecycle:
- Archived directory —
aitasks/archived/t150_feature.md(loose files, recent) - Numbered archives —
aitasks/archived/_b0/old1.tar.gz(compressed bundles)
The numbering scheme groups tasks by hundreds:
| Task IDs | Bundle | Directory | Archive Path |
|---|---|---|---|
| 0–99 | 0 | 0 | archived/_b0/old0.tar.gz |
| 100–199 | 1 | 0 | archived/_b0/old1.tar.gz |
| 900–999 | 9 | 0 | archived/_b0/old9.tar.gz |
| 1000–1099 | 10 | 1 | archived/_b1/old10.tar.gz |
Computation:
bundle = task_id / 100(integer division)directory = bundle / 10(integer division)path = archived/_b{directory}/old{bundle}.tar.gz
The _b prefix on directory names avoids collision with task child directories (t<N>/).
Child tasks are archived with their parent’s bundle (e.g., t130/t130_2_subtask.md goes into old1.tar.gz alongside t130_feature.md).
Plan archives follow the same scheme under aiplans/archived/.
Customizing Task Types
Valid issue types are defined in aitasks/metadata/task_types.txt (one type per line, sorted alphabetically). The default types are:
bug
chore
documentation
enhancement
feature
performance
refactor
style
test
manual_verification
To add a custom type, simply add a new line to the file. All scripts (ait create, ait update, ait board, ait stats) read from this file dynamically.
Next: Review Guide Format