field_schema, split into a task section and a comments section. A task’s values for those fields live in custom_data. Fields are the vocabulary your policies speak — a transition_guard checks field values to decide whether a move is allowed.
Defining a field
Declare fields directly in the board’sfield_schema.task object. Here is an example covering every available type:
Field properties
string
required
The data type for this field. One of:
string, number, boolean, enum, markdown, string_list.string[]
The allowed values when
type is enum. Required for enum fields — a board that declares an enum without values fails validation.boolean
Marks the field as required. Enforcement is lazy: existing tasks are not retroactively rejected, but
list_tasks accepts a missing_required_fields filter to surface tasks a tightened schema left incomplete.string
A free-form format hint. Informational only — Substrate does not enforce it.
boolean
When
true, agent MCP write tools refuse to set this field. Only substrate approve can write it. See Human-only fields below.Validation is lazy
Substrate validatescustom_data at write time, not retroactively. Changing a board’s schema never rejects data already stored — add, tighten, or remove a field without running a migration. Use the missing_required_fields filter on list_tasks to find tasks that a schema change left incomplete.
Changing a field
update_board with a full field_schema replaces the entire schema at once. To change a single field without touching the rest, use field_schema_patch — it merges the fields you supply, and setting a field to null deletes it:
Gate fields
A gate field is a boolean you agree to treat as an exit criterion —spec_approved, tests_passing, review_cleared. An agent flips it via update_task as each criterion is met, and a transition_guard policy requires it to be true before allowing a group move. Reference a gate field from a policy condition as task.<name>.
A "*" → done guard that requires tests_passing: true is the idiomatic definition-of-done gate.
Human-only fields
When a field carries"human_only": true:
create_taskandupdate_taskrefuse to set it — the write returnsforbidden.update_boardrefuses to downgrade or delete the flag — an agent cannot strip its own restriction.- The only write channel is the CLI:
substrate approve <task_id> <field>. The change is stampedhuman:<os-user>in the event log. substrate unapproverevokes the approval by deleting the key, so both anexistscheck and aneq truecheck re-block correctly.
Comments fields
human_only applies to task fields only. On comments fields it is currently inert.Policy is still data
The guard policy is substrate-as-code. An agent could rewrite or
archive_policy the guard — that’s a git-visible, event-logged act, but not structurally impossible.