> ## Documentation Index
> Fetch the complete documentation index at: https://docs.substrate.42.pe/llms.txt
> Use this file to discover all available pages before exploring further.

# Boards, Groups, Tasks, and Comments: Substrate Data Model

> Understand how Substrate organizes work: boards, explicit group ordering, versioned tasks, threaded comments, and a full append-only event log.

Everything in Substrate is organized around a single project that holds any number of boards. A board models one workflow — its own stages, custom fields, and rules — and tasks move through it left to right. Understanding the hierarchy and the version discipline keeps agents and people working on the same source of truth.

## Project

One `.substrate/` directory is one project with an id, name, description, and schema version. Read it with `get_project`; update it with `update_project`. A project holds any number of boards.

## Boards

A board is a workspace: one workflow, its own stages, its own custom fields, its own rules. Boards carry an id, name, description, `field_schema`, groups, policies, an optional team binding, and a version. Most projects work best with a small number of boards, each modelling a distinct flow.

<Tip>
  Prefer extending an existing board over creating a new one. Two boards that model the same flow is the most common way a Substrate project stops being a single source of truth.
</Tip>

Boards are soft-deleted: `archive_board` sets `archived_at`; `unarchive_board` clears it. Both operations are idempotent.

## Groups

A group is a column on the board. Ordering is explicit: the `position` field (0, 1, 2, …) defines left-to-right flow.

* `create_group` appends by default; pass a `position` to place it at a specific index.
* `reorder_groups` sets the entire order at once — it requires exactly the board's current group ids.
* `archive_group` is refused with `conflict` if active tasks still reference it — move them first.

Keep group ids short, stable, and readable (`spec`, `build`, `done`). They are referenced by every task and by every policy, so changing them later has wide consequences.

## Tasks

A task belongs to one group at a time and moves through groups as work progresses. Moving a task is an update: call `update_task` with a new `group_id`. That is precisely the write that a `transition_guard` policy inspects.

| Field         | Notes                                                                |
| ------------- | -------------------------------------------------------------------- |
| `title`       | Required.                                                            |
| `description` | Markdown.                                                            |
| `custom_data` | Your board's custom fields, validated lazily against `field_schema`. |
| `group_id`    | Moving between groups is what policies guard.                        |
| `parent_id`   | Optional — creates a subtask relationship.                           |
| `version`     | Echo it back on every update, or receive `version_mismatch`.         |

`archive_task` soft-deletes a task with its history preserved; `unarchive_task` restores it. Nothing in Substrate hard-deletes a task.

### Reading tasks without flooding context

Choose the view that matches your intent:

<CardGroup cols={2}>
  <Card title="titles" icon="list">
    Leanest rows — use this for pure selection when you only need to identify tasks.
  </Card>

  <Card title="summary" icon="align-left">
    The default view — balanced detail for most agent reads.
  </Card>

  <Card title="full" icon="file-text">
    Complete description and `custom_data` — use when you need everything.
  </Card>

  <Card title="get_task(id)" icon="crosshairs">
    Fetches one task in full — use when you already have the id.
  </Card>
</CardGroup>

## Comments

Comments are threaded (a `parent_id` makes a reply), markdown-bodied, and can carry `custom_data` validated against `field_schema.comments`. `edit_comment` is last-write-wins — it takes no version, and the write envelope version is `null`.

<Tip>
  Use a comment for a decision, rationale, or a note to the next session. Use a custom field for anything a policy needs to read — policies cannot inspect comment content.
</Tip>

## The event log

Every write appends an entry to the event log. Read it with `get_task_history`.

| Event           | What it captures                                                           |
| --------------- | -------------------------------------------------------------------------- |
| `created`       | Initial state — group, title, `custom_data`                                |
| `updated`       | Before/after state, plus `policies_fired` list when a policy engaged       |
| `archived`      | Soft-delete timestamp and actor                                            |
| `move_blocked`  | The `transition_guard` that rejected a move, including the failure message |
| `comment_added` | Comment body and author                                                    |

A human approval via `substrate approve` is stamped with a `human:<os-user>` actor, making it distinguishable from any agent write in the log.
