Task File Format

YAML frontmatter schema and conventions for task files

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

FieldValuesDescription
priorityhigh, medium, lowTask priority for sorting
effortlow, medium, highEstimated implementation effort
depends[1, 4]List of task numbers this depends on
xdeprepoproject nameThe 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 xdeprepoxdeps alone is rejected at create and update time, because task IDs are meaningless without a project to resolve them against
issue_typebug, chore, documentation, enhancement, feature, manual_verification, performance, refactor, style, testType of work (from task_types.txt)
statusReady, Editing, Implementing, Postponed, Done, FoldedCurrent status
labels[ui, backend]Categorization labels
created_atYYYY-MM-DD HH:MMCreation timestamp
updated_atYYYY-MM-DD HH:MMLast modification timestamp
completed_atYYYY-MM-DD HH:MMCompletion timestamp (set on archival)
archived_reasonsupersededWhy 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_toemail addressDeveloper working on the task
issueURLLinked GitHub/GitLab/Bitbucket issue
children_to_implement[t10_1, t10_2]Remaining child tasks (parent tasks only)
boardcolcolumn IDBoard UI column placement
boardidxintegerBoard UI sort index within column
boardgroupslug ([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_intotask numberTask this was folded into (set by /aitask-fold or /aitask-explore)
anchortask 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_kindmanual_verification, risk_mitigation, upstream_defect, verification_failure, carry_over, qa_test_gap, review_finding, docs_gapMarks 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
artifactslist of mappingsNested. 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
attachmentslist of mappingsNested. 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:MMThe 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_atYYYY-MM-DD HH:MMMarks 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_healthhigh, medium, lowCode-health risk (stability, quality, maintainability, blast-radius) assigned by the risk-evaluation planning step; display-only, omitted unless evaluated
risk_goal_achievementhigh, medium, lowGoal-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_requestURLLinked pull request URL (mirrors issue)
contributornameExternal contributor credited on the commit (used by PR-import flow)
contributor_emailemailEmail 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_profileprofile nameFramework-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-fold or /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:

  1. Archived directoryaitasks/archived/t150_feature.md (loose files, recent)
  2. Numbered archivesaitasks/archived/_b0/old1.tar.gz (compressed bundles)

The numbering scheme groups tasks by hundreds:

Task IDsBundleDirectoryArchive Path
0–9900archived/_b0/old0.tar.gz
100–19910archived/_b0/old1.tar.gz
900–99990archived/_b0/old9.tar.gz
1000–1099101archived/_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