Skip to main content
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.
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.
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. 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:

titles

Leanest rows — use this for pure selection when you only need to identify tasks.

summary

The default view — balanced detail for most agent reads.

full

Complete description and custom_data — use when you need everything.

get_task(id)

Fetches one task in full — use when you already have the id.

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.
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.

The event log

Every write appends an entry to the event log. Read it with get_task_history. A human approval via substrate approve is stamped with a human:<os-user> actor, making it distinguishable from any agent write in the log.