Specification

Epic, story, task & bug files

The board UI and agent commands read the same markdown files. Commands generate them; you can also create or edit them by hand — as long as you keep the specification below.

Why this matters

Taskmark stores product memory as plain markdown under the board root. The local board UI parses these files by scanning epics/ — not a generated index. Cursor commands (/tkmd-plan, /tkmd-save, /tkmd-save-do, /tkmd-plan-do, /tkmd-do, /tkmd-verify) generate the same shape.

That means you may:

  • Let the agent create items via slash commands
  • Hand-write or tweak a file in your editor
  • Fix typos, AC checkboxes, or copy without running a command

If frontmatter keys or exact section headings drift, parsers can miss status or work logs. Treat this page as the contract between you, the UI, and the agent.

Common rules for every item

  • File starts with YAML frontmatter between --- fences
  • First markdown heading is # {ID}: {Title}
  • New IDs are unique board-wide: E|S|T|B-<identity>-<random> (for example T-MM-a8f31c2d). Legacy sequential IDs such as T-001 already on disk remain valid
  • Folder slug: {id}-{kebab-title} (see folder structure)
  • Keep section headings exact (spelling and level)
  • Tasks and bugs have Commits and Work log tables (even if empty), plus Prompt & feedback
  • Do not maintain child lists, rollups, or logs on parent files — the UI derives those at read time from descendant leaves

Frontmatter fields

Shared shape (types differ on size / parents — see below):

---
id: T-MM-a8f31c2d
type: task          # epic | story | task | bug
title: Add login API endpoint
status: backlog     # backlog | blocked | in_progress | done | shelved | cancelled
priority: medium    # critical | high | medium | low
size: M             # XS | S | M | L | XL | XXL | null (null on parents with children)
points: 5           # 1 | 3 | 5 | 8 | 13 | 21 | null
parent: S-MM-n4q8w1a2    # null on epics; story/epic id otherwise
epic: E-MM-k7m2p9b3      # null on epics; ancestor epic otherwise
reporters: []       # [{name, email, initials}, ...]
resolvers: []
blocked: false
cancelled: false
tags: []
created: 2026-07-21
updated: 2026-07-21T15:02:00Z
started_at: null
completed_at: null
---

What to edit by hand vs leave alone

Safe to editLeave to commands / UI
title, priority, tags, body copy, AC checkboxes [ ]/[x], Notesstatus (use blocked / cancelled latches or /tkmd-shelf instead), Actual (the UI derives it from closed leaf Work log intervals)
Leaf size / points from the static map (do not add estimate or owner fields)Parent points, status, people, and dates (UI queries children)
Leaf Prompt & feedback, Commits, and Work log tablesChild lists on epic.md / story.md — do not maintain them; /tkmd-plan does not write them

Status on a leaf: done means implemented; shelved means deliberately discarded without implementation; cancelled is a separate terminal latch outcome. Backlog, blocked, and in-progress work is incomplete. /tkmd-do does not transition through in_progress; when it finishes, executed leaves are done. /tkmd-shelf changes only eligible task/bug leaves to shelved, sets completed_at, preserves acceptance criteria and cancelled: false, and never edits parent markdown. Parent status, implementers, and lifecycle dates are inferred from leaves in the UI: a terminal mix containing done rolls up to done; without done, any shelved descendant rolls up to shelved; all-cancelled rolls up to cancelled. Hide completed treats all three terminal outcomes as complete.

Epic — epic.md

Path: epics/{id}-{slug}/epic.md

  • type: epic, parent: null, epic: null
  • size: null — epics have no t-shirt size
  • Points / Actual / logs / child lists are read-time views over descendant files — do not write rollups or story lists here

Required body sections (keep headings exact):

# E-MM-k7m2p9b3: Title here

## Goal

## Scope

## Out of scope

## Success metrics

Story — story.md

Path: epics/…/stories/{id}-{slug}/story.md

  • type: story, parent = epic id, epic = same epic id
  • /tkmd-plan attaches to an existing fitting epic; it creates a new epic only when the initiative fits nowhere. There is no reserved General epic to soft-attach to
  • Tasks live in this story’s items/; the UI lists them without a maintained Tasks section
# S-MM-n4q8w1a2: Title here

## User story

As a …, I want … so that ….

## Acceptance criteria

- [ ] Criterion one
- [ ] Criterion two

The board UI treats unchecked - [ ] vs checked - [x] under Acceptance criteria as the source of leaf completion (together with Work log state on the item).

Task — T-<identity>-<random>.md

Path (under story): …/stories/…/items/{id}-{slug}.md
Path (epic-direct): epics/{epic-id}-{slug}/items/{id}-{slug}.md

  • Under story: parent = story id, epic = ancestor epic
  • Epic-direct: parent and epic both = epic id (never leave parent: null)
# T-MM-a8f31c2d: Title here

## Description

What to build or change.

## Acceptance criteria

- [ ] Observable outcome one
- [ ] Observable outcome two

## Notes

Optional implementation notes, links, or decisions.

## Prompt & feedback

| When (UTC) | Kind | Author | Summary |
|------------|------|--------|---------|

## Commits

| SHA | Repo | Date (UTC) | Author | Message |
|-----|------|------------|--------|---------|

## Work log

| Actor | Started (UTC) | Ended (UTC) | Summary |
|-------|---------------|-------------|---------|

Bug — B-<identity>-<random>.md

Same location rules as tasks. Differences:

  • type: bug, id prefix B
  • Prefer sections Description, Repro steps, and Fix criteria (checkboxes under Fix criteria drive done, like AC on tasks)
  • Still include Prompt & feedback, Commits, and Work log
# B-MM-f8h1j4c5: Title here

## Description

What is wrong / expected vs actual.

## Repro steps

1. …
2. …

## Fix criteria

- [ ] Failure no longer reproduces
- [ ] Regression covered or noted

## Notes

## Prompt & feedback

| When (UTC) | Kind | Author | Summary |
|------------|------|--------|---------|

## Commits

| SHA | Repo | Date (UTC) | Author | Message |
|-----|------|------------|--------|---------|

## Work log

| Actor | Started (UTC) | Ended (UTC) | Summary |
|-------|---------------|-------------|---------|

Table formats the UI expects

Work log

| Actor | Started (UTC) | Ended (UTC) | Summary |
|-------|---------------|-------------|---------|
| Marco Mendão | 2026-07-28T10:00:00Z | 2026-07-28T10:40:00Z | Implemented route |
  • Open interval = Ended is empty
  • Actual minutes are computed from closed rows on the leaf
  • A legacy Session column still parses if present; new files omit it

Commits

| SHA | Repo | Date (UTC) | Author | Message |
|-----|------|------------|--------|---------|
| a1b2c3d | taskmark-frontend | 2026-07-28T12:00:00Z | Ada | fix login redirect |

In multi-repo workspaces, Repo is the short folder name from local REPOS.md.

Prompt & feedback

| When (UTC) | Kind | Author | Summary |
|------------|------|--------|---------|
| 2026-07-28T10:00:00Z | prompt | Marco Mendão | Add login redirect |
| 2026-07-28T11:50:00Z | feedback | Marco Mendão | Looks good |

Kind is typically prompt or feedback. Agents write a prompt row on every product-work session: an open matching leaf, a done leaf that still covers the change, or a newly created task/bug. Never on epic or story parents.

Manual create / edit checklist

  1. Mint a collision-resistant id (E|S|T|B-<identity>-<random>). Do not take “the next number” from a global sequence
  2. Create the folder/file in the correct place (structure)
  3. Copy the matching template above; fill title and body
  4. Set parent / epic correctly. Do not add the child to a parent Stories/Tasks list
  5. Do not add estimate or owner fields. Actual comes only from real, closed Work log intervals

Related: Sizing, Commands, Workflows.