Task System (Recurrence & Multi-Spawn)
Bowerbird’s task system is declarative: recurrence and templating together make a real task system out of the schema itself. There is no cron, no daemon, and no LLM — the schema is the system.
It has two mechanisms:
- Recurrence — spawn a successor note when a field transitions (e.g.
statusentersdone). - Multi-spawn templating — a parent template that scaffolds several staggered child notes on creation.
Recurrence: spawn-on-transition
Section titled “Recurrence: spawn-on-transition”The trigger is a field transition, not a clock. Declaratively: “when status enters
done, spawn a successor from a template, with the successor’s date field offset from a
predecessor date field.”
Recurrence rides on a trait (see
Types and Inheritance). Add a recurrence block to a
trait, and any type that composes that trait recurs.
# .bwrb/schema.json (shown as YAML for readability)traits: recurring: fields: next: { prompt: relation, source: task } # the chain field (see below) prev: { prompt: relation, source: task } recurrence: on: "status = done" # trigger transition template: <name> # optional; defaults to the type's default template set: deadline: "deadline + 7d" # FIELD-OFFSET ONLY; base must be a date field
types: task: output_dir: tasks traits: [recurring] fields: name: { prompt: text, required: true } status: { prompt: select, options: [todo, doing, done], default: todo } deadline: { prompt: date }Completing a task now spawns its successor automatically.
The date rule: field-offset only
Section titled “The date rule: field-offset only”The successor’s date is computed as:
successor.<dateField> = <predecessor date field> + <offset> e.g. deadline + 7d- The referenced base must be a date field.
- Transition-time offsets (e.g. “a week after I actually finished”) are not supported — they can only be exact on the immediate write, never reproducible by the audit backstop, so they would be inconsistent. Field-offset is computed identically on both paths.
- Calendar-anchored bases (“next Monday”) are deferred.
Offsets accept min, h, d, w, mon (or m), and y. Day/week offsets are exact;
month/year offsets are calendar-aware (e.g. deadline + 1mon lands on the same day next
month).
The template
Section titled “The template”The successor’s template defaults to the completed note’s type default template (a
task begets a task). Naming a template can spawn a different type — finish a draft,
spawn a review:
recurrence: on: "status = done" template: review-checklist # a template whose template-for is `review`The audit validates that the referenced template exists (a rule pointing at a deleted template is a deterministic error — see below).
Successor naming. By default the successor carries the predecessor’s name so the chain
reads naturally, but bwrb wikilinks are name-based ([[Chapter One]] resolves by
basename across every directory). So the spawned successor’s basename is guaranteed
unique across the whole vault: if the carried name is already taken anywhere, bwrb
appends 2, 3, … A same-type successor in a busy directory and a cross-type
successor (finish a draft → spawn a review) are handled the same way — finishing
drafts/Chapter One.md spawns reviews/Chapter One 2.md, not reviews/Chapter One.md, so
the predecessor’s next and the successor’s prev resolve unambiguously to the right notes
(a type whose template defines a filename pattern disambiguates via that pattern instead).
Naming a cross-type successor (name_template). A numeric suffix keeps links
unambiguous but is cosmetically ugly and decouples the name from a meaningful title. Set
name_template on the recurrence rule to give the successor a meaningful, distinct name
instead:
recurrence: on: "status = done" template: review-checklist # spawns a `review` name_template: "Review: {name}" # → "Review Chapter One"The template is interpolated with the same tokens used for filename patterns —
{name} (the predecessor’s name), {date} / {date:FORMAT} (today), and any predecessor
field {field} — then sanitized for a filename (the : is filesystem-illegal, so
Review: Chapter One is filed as Review Chapter One). Finishing drafts/Chapter One.md
now spawns reviews/Review Chapter One.md.
Vault-global uniqueness still applies on top of the result: if the interpolated name
also collides (e.g. another Review Chapter One already exists), bwrb appends the same
2, 3, … suffix, so next/prev always resolve to the right notes. If the template
references a token the predecessor doesn’t have, bwrb falls back to the carried name rather
than failing the spawn. Omit name_template to keep the carried-name + numeric-suffix
behavior described above.
The next field does three jobs
Section titled “The next field does three jobs”A single next relation field collapses three requirements into one mechanism:
- Graph chain / history — the linked thread of recurring instances, for free. The
spawned successor’s
prevlinks back to its predecessor. - Idempotency — a successor is spawned only if
nextis empty. Re-completing a task that already has anextis a no-op. - Audit backstop check — “missing successor” is literally trigger satisfied +
nextempty + type recurs.
Two-path execution (reliability)
Section titled “Two-path execution (reliability)”Recurrence integrity holds regardless of how a task was completed.
Fast path
Section titled “Fast path”Completing a recurring note through bwrb spawns the successor immediately as a deterministic side-effect of the write:
bwrb edit "tasks/Water plants.md" --json '{"status":"done"}'# orbwrb bulk --type task --where "status == 'doing'" --set status=done --executeEither spawns one successor with the offset deadline and the next/prev chain links.
Backstop (audit)
Section titled “Backstop (audit)”Completing a note outside bwrb (e.g. hand-editing frontmatter in Obsidian) is caught on the next audit:
bwrb audit --path "tasks/**" # flags: missing-successorbwrb audit --path "tasks/**" --fix # spawns the missing successorbwrb audit --all --fix --auto --execute # headless: spawn all missing successorsThe backstop uses the same engine as the fast path, so it produces an identical successor (same offset date, same chain links). Both paths are idempotent: re-running never spawns a duplicate.
Tip for AI/agent workflows: always edit note fields via bwrb, never by writing frontmatter directly. That feeds the fast path and keeps the chain consistent in real time. The audit backstop is there for everything else.
Config validation
Section titled “Config validation”A broken recurrence rule is a deterministic config error, surfaced by bwrb audit as
invalid-recurrence (never auto-fixed):
- a malformed
ontrigger, - an offset whose base is not a date field,
- a
templatethat does not exist in the vault.
Multi-spawn templating
Section titled “Multi-spawn templating”A parent template can scaffold several related child notes on creation, with staggered deadlines computed from today via date expressions:
---type: templatetemplate-for: projectinstances: - { type: task, defaults: { name: "Outline", deadline: "@today+1d" } } - { type: task, defaults: { name: "Draft", deadline: "@today+3d" } } - { type: task, defaults: { name: "Edit", deadline: "@today+5d" } } - { type: task, defaults: { name: "Publish", deadline: "@today+7d" } }---bwrb new project --template write-an-articleThis creates the project and four staggered task notes, each with a distinct,
meaningful filename derived from its name (no more collapsing into a single
task.md). Multiple instances of the same type are automatically disambiguated.
[!IMPORTANT]
defaultsmust use the child type’s real fields. Here thetasktype’s required field isname, so each instance setsname(nottitle). Using a field the child type doesn’t declare would make every scaffolded note failbwrb auditwithunknown-fieldandmissing-required.
Each scaffolded instance is filed in its own child type’s output_dir (here
tasks/), not the parent project’s directory — so the multi-spawn workflow stays
bwrb audit-clean (no wrong-directory).
Synthesis
Section titled “Synthesis”- Templates define what work looks like (the staggered plan).
- Recurrence rules define when work regenerates (on completion).
- bwrb executes deterministically (fast path) and
auditbackstops it.
The whole system is declared in schema + templates, so an agent operates it natively. No daemon, no cron, no LLM.