Skip to content

Relationship pickers

Relationship pickers consume bob's additive contract and never parse quoted note components themselves: bob sends the decoded query, the owner, exact paths, groups, hidden and already-added flags, disabled reasons, and the backend-formatted replacement, which is empty for ID-less and guarded rows. The shared card lifecycle is in Pickers; rendered rows are in Capture previews.

Dependency Picker

Typing & at the start of an item or after whitespace opens the Dependency Picker (task_dependency context), the same card with scope & and caption Depends On. It consumes Bob's additive contract and never parses quoted note components itself: Bob sends the decoded query, the lexical owner (new_task / existing_task), the exact note_path (extension included, case and Unicode preserved), the display locator, the group, hidden, already_dependency, and disabled_reason per row, plus a Bob-authored replacement (&note:id, quoted when the locator needs it) that is empty for ID-less and guarded rows.

The header names the owner and direction: ⛓ Exercise depends on… with body.md ^excercise · In Progress beneath, or ⛓ “Buy Groceries!” depends on… for a new-task owner (empty new-task text is New task depends on…). An older Bob that omits owner text falls back to @body+excercise. For an ownerless leading &, the title stays Choose a prerequisite and the subtitle is "Then type the task, or + for an existing one" — choosing first stays allowed. No word appears twice in the header. With an empty filter rows group in Bob's order: This note (in_this_note) first, then In Progress, Next, one section per note (note_path), then a separate Completed history section (selecting there never blocks); #hide rows render subdued and sort last within their section. A non-empty filter replaces the groups with one ranked flat list. Already-added rows show Already added and accepting one changes nothing; guarded rows carry Bob's explanation and neither insert nor open the ID flow.

When capture-parse reports dependency_target with no open picker (a complete &foo:bar and no owner yet), the app skips the doomed live dry run and shows a calm secondary hint instead of a red preview error: ⛓ What depends on “Buy shoes”? Type a new task, or + for an existing one. Names come from &/* rows the user accepted; a token typed by hand falls back to foo:bar. The other shapes are 🧱 What blocks “Do cleanup tasks”? Type a new task, or + for an existing one and Type the task between “Buy shoes” and “Do cleanup tasks”, or + for an existing one.

Return (or Tab) replaces only Bob's range with the replacement and returns to the editor; at a terminal token one separating space is left for typing another &. Command-Return inserts then captures only when the dependent is complete; Control-Return also opens the reported notes when the dependent is complete; with no dependent yet it keeps the draft open ("Add task text or @note+id, then capture"). Shift-Return has no start-session behavior on this source. Escape restores the draft/caret and shows the Choose dependency chip; Backspace on an empty filter removes the & token.

Rows without an ID show locator: plus a dim suggestion; accepting one opens the Add block ID prompt ("Add ID and use task — edits that note now") in dependency mode, calling bob capture-task-id --note-path with the exact path (passing --allow-closed for Done/Cancelled history rows, which never reopens them) and splicing Bob's dependency_replacement verbatim. Escape returns to the picker with the filter intact; assignment failure inserts nothing. The prompt buttons read Add ID & Use / Add ID & Capture.

Dependency Picker keys: Return/Tab Insert, Command-Return Insert & Capture, Control-Return Insert, Capture & Open when the dependent is complete.

Blocks Picker

Typing * in Bob's valid blocked-task position opens the shared dependency picker in the Blocks direction (blocked_task context): scope *, caption Blocks, and the header 🧱 Exercise blocks… with the owner location line, 🧱 “Buy Groceries!” blocks… for a new-task owner, or Choose a task to block when there is no owner yet. Bob supplies the query, owner, candidates, already_blocked, disabled reasons, and backend-formatted *note:id replacements. The action line reads ↩ inserts *job:cleanup · space adds another *; rows already on the owner's Depends-On line say Already blocked. Disabled explanations appear verbatim from Bob.

The partial X * need shows Pick a task to block — press Tab to browse. Return or Tab inserts Bob's replacement, Command-Return inserts then captures when the owner is complete, Control-Return also opens the reported notes, and Backspace on an empty filter removes the * token. A line-initial * stays a Markdown bullet placeholder. ID-less rows use capture-task-id --note-path and splice blocked_task_replacement; older Bob shows This Bob is too old for blocked-task picks. Update Bob first.

Blocks Picker keys: Return/Tab Insert, Command-Return Insert & Capture, Control-Return Insert, Capture & Open when the owner is complete.

Relation Picker

Typing ~ in Bob's valid relation position opens the Links To picker (relation_to context): scope ~, caption Links To, and the header 🪢 “Owner” links to… (or 🪢 Choose a task to link to… when there is no owner yet). The subtitle is any task · ~ flips direction. Typing a second ~ at the same replacement start live-flips to Linked From (relation_from): scope ~~, caption Linked From, and the header 🪢 Choose a task that will link to “Owner”. The filter stays; auto-open is never suppressed. Empty-filter ~ is operator continuation, like Complete's !. Backspace on an empty ~~ filter flips back by removing one ~; Backspace on empty-filter ~ removes the token.

Bob supplies already_linked, existing_label, also_prerequisite, disabled reasons, and backend-formatted ~note:id / ~~note:id replacements. Already-linked rows show Already linked · follow-up of when Bob sends an existing label. Prerequisite-too rows show Prerequisite too. Archived rows (locator done/ or /archive) show Archived. Done history rows show Done. Disabled explanations appear verbatim from Bob (its LINKS line is malformed, read-only note, archived tasks can't get an id).

The action line reads ↩ link · ⇧↩ link with a relation · ~ flips direction. Shift-Return inserts the token and opens the relation card. Typing | after a relation token also opens the card; Bob removes any spaces or tabs before the pipe through the rewrite lane, and Undo restores the typed |. The card keeps its query field separate from the highlighted label and filters the catalog fetched when it opens. Ctrl-N/P and Down/Up move the highlight, wrapping through the field. Tab inserts the selected label, or the top match when nothing is selected. Return inserts the selected label, or uses Bob's fresh typed-label replacement; with an empty field it removes the pipe and leaves the bare relation token. Shift-Tab is consumed. Escape returns to the unchanged draft. Command-Return inserts then captures when the owner is complete; Control-Return also opens the reported notes. The ~ / ~~ sigil uses a mint knot accent, distinct from the grey Pomodoro-drop ~. Relation labels render muted italic; the | separator is muted.

The partial X ~ need shows Pick a task to link — press Tab to browse. A |label field shows Type a relation label, or pick a suggestion. Owner-mode + copy uses picker.relationship.relations[] (🪢 Which task links to “Target”?).

ID-less rows use capture-task-id --note-path and splice relation_to_replacement / relation_from_replacement; older Bob shows This Bob is too old for ~ links. Update Bob first. The prompt buttons read Add ID & Use / Add ID & Capture.

Relation Picker keys: Return/Tab Link, Shift-Return Link with a relation (opens the relation card), tilde Flip direction, Command-Return Insert & Capture, Control-Return Insert, Capture & Open when the owner is complete.

Relation card keys: Ctrl-N/Down next, Ctrl-P/Up previous, Tab accept, Return accept selected or typed label, Ctrl-D hide what Return would insert (or toggle one-off), typed | one-off on, Ctrl-H Hidden section, Escape back. The card opens in one-off mode on a || token. Hidden labels live in the Hidden section and stay insertable; a hide or unhide keeps its row as a ghost with ⌃D undo until the query changes, and a failed write restores the row with Bob's message. See Keyboard for the complete key table.

Complete Picker

Typing ! at the start of an item opens the Complete picker (task_complete context), the same card with scope ! and caption Complete. When capture-parse reports task_complete in needs, the app skips the doomed live dry run and shows "Pick a task to complete — press Tab to browse".

Bob lists every open task (Ready, Blocked, Next, In Progress) vault-wide, today first. With an empty filter rows group in Bob's order: one section per today Pomodoro (the running entry with the pink NOW pill, completed entries recent-first with 🍅, their time range, and a Done capsule, queued entries with UP NEXT), then "In today's note", In Progress, Next, and one section per note (note_path with a doc icon). While filtering, two ranked sections appear with their headers: Today then All open tasks. Rows show the status glyph (Blocked is pause.circle), text, and locator (note in accent, : , ID in indigo), a 🍅 N capsule when the task was in two or more Pomodoros today, and a schedule capsule when present. Disabled rows stay visible with their reason badge and no insertion; recurring rows sort last. The detail strip renders Bob's action line for the selected row (Inserts !sase:fix-flaky — completes it [*] → [x], with a Blocked variant and the disabled reason).

Return (or Tab) inserts Bob's !note:block-id; Shift-Return inserts, appends a blank line plus !, and the fresh picker opens with the just-picked task marked "Already in this draft" for bulk completions; Command-Return inserts, then captures; Control-Return inserts, captures, and opens Bob's reported notes. With an empty filter, Bob's continuation keys ! and [ close the picker and type the key, so !! falls back to prose and ![ hands off to embed completion. Escape and Backspace behave as in :.

ID-less rows open the Add block ID prompt via capture-task-id --note-path and splice Bob's complete_replacement verbatim; Swift never builds the token. The prompt previews !<locator>:<id> from Bob's candidate locator, never the file name. The buttons read Add ID & Insert and Add ID & Complete.

Complete Picker keys: Return/Tab Insert, Command-Return Insert & Capture, Control-Return Insert, Capture & Open.

Preview rows, notifications, and the successor choice card are in Capture previews.

Tested rolling snapshot bob-cli 41afa14 bob-mac-capture 556bb67 bob-plugins af1e691 Built 2026-10-24T22:34:13Z (zensical) build.json