Skip to content

Capture previews

Every card below renders what bob capture --dry-run --no-clip --format json returns for the current draft, in bob's own order. The explicit Preview button and Capture resolve the clipboard and mirror the full block; continuous live preview passes --no-clip. Insertion behavior for the rows is in Task pickers and Relationship pickers.

Task completion card showing the completed task and its struck Task Link in the running Pomodoro Task completion card showing the completed task and its struck Task Link in the running Pomodoro

Synthetic CI render. Open the image for full-size text.

Start overrides

A = override restart or swap previews on the same start card with an orange warning banner as the first row, reworded titles, status, badges, footer action, and notifications, built only from Bob's additive pomodoro_start.override object (action restart/swap, leftover "start" as a plain start, ledger kept/fresh, previous including overdue, demoted, close_first). An older Bob that omits the object decodes as a plain = start with no behavior change, and an unknown override action degrades to a plain start; the app never computes ledger ranges itself. The footer's primary Return tints orange whenever the on-screen preview contains an override. An override commits only after its warning was on screen: every real submit passes hidden --refuse-override unless that preview belongs to this exact draft, analysis generation, and bob client and shows the override. A pomodoro_override_refused answer keeps the draft, re-previews, and asks for Return again. An older bob that rejects the flag is retried without it and that answer is remembered for that executable.

Templates

A Bob template candidate for @@te or @@ is shown as the @@template: directive with Bob's label beneath it; accepting inserts Bob's replacement verbatim. The + shortcut does not accept this header candidate. When the route list includes a template candidate at bare @@, its header shows that typing @ again starts a template.

Template fields decode tolerantly: an older Bob omits them and keeps ordinary drafts working. A new Bob with an older app exposes unknown template span kinds as neutral text. An older Bob refuses @@template: before writing anything, so upgrade Bob before submitting a template draft.

Template drafts live in the Saved templates shelf.

Live preview

Live preview calls bob capture --dry-run --no-clip --format json -- <draft> through a dedicated process-client API that asserts --no-clip. Bob's admitted URL % listen marker remains a reference request in continuous preview; ordinary clipboard spans stay literal until the separate explicit clipboard-resolving preview. Template previews show a Template · N rows capsule beside the capture count. When Bob reports the template_row need, the panel keeps the preview calm and shows Bob's template_missing_rows message. Non-info template diagnostics underline the matching typed text in the editor.

Batch drafts and authored children

The additive sub_bullets field on capture output is the exact rendered authored child lines, including Bob's target-selected indentation, and is omitted entirely when a draft has no authored bullets. capture-parse keeps sub_bullets as normalized bodies and adds aligned sub_bullet_depths values (1 or 2); missing depths from an older bob decode as depth 1, and mismatched depths are ignored safely. capture-parse also decodes Bob's additive items array for batch drafts, preserving item index, source range, physical line range, route, section, needs, and authored-child depths. Its additive global_destination object is decoded tolerantly; absent metadata means the connected bob binary predates global destination declarations. Template parsing adds a nullable header/range/field/row object and per-row items with UTF-8 source ranges. Template rows never open pickers; a row-incomplete need keeps the dry-run preview idle until a row is added. Capture responses add a template summary and per-capture row numbers, including the legacy one-result response shape.

Capture results

bob capture --format json keeps the legacy top-level first result for a single capture or compatibility fallback, and may add an ordered captures array for a batch. The app normalizes both shapes to one collection before updating preview, status, VoiceOver announcements, and notification content; it forwards the batch-level open_notes array unchanged for Control-Return instead of re-deriving destinations. It keeps the additive top-level global_destination summary alongside that normalized collection and never splits a draft into multiple mutating bob subprocesses. When live preview reports exactly one task_toggle, the footer's primary action changes from Capture to Set Next, Set Open, or Ensure Next according to Bob's returned behavior metadata; when it reports exactly one pomodoro_link, the action becomes Start if the link starts a session and Link otherwise; a single close — whole-item x, link =x, or new-task =x — becomes Close; a single whole-item =/=<X> start becomes Start, a = restart of the running session becomes Restart, and a =#name swap becomes Swap; a single whole-item !note:block-id completion becomes Complete; batches keep Capture because Return will submit more than the toggle. Ensure Next preview, VoiceOver, and notifications present status and relocation independently ("Ready → Next" vs "Next unchanged", "Moved LATER → CURRENT" vs "Already in CURRENT; no Pomodoro changes") and never show the two-way "adds link" or "removed later links" rows. Committed notification titles summarize the actual result (combined, status-only, move-only, or already-Next no-op); Open Note/Open Notes includes the daily note only when pomodoro_link_action is moved. Pomodoro-link captures notify as Started NAME, Linked to NAME, Moved to NAME, or Already in NAME, batch lines use the link transition text under a Link kind, and Open Note/Open Notes includes the daily note whenever the link inserted, moved, or started a session. A Pomodoro close notifies as Closed NAME with a session-and-timing line, a tasks-and-Work-Log line (with a completed count when nonzero), and the next-session line; link and new-task closes use the same title and body, batch lines append (closed NAME) under a Close kind, and Open Note targets the day file, which a close always writes. A project note that links tasks previews a link section ("Links 2 tasks into ADMIN (new)", one row per task with its text and ^id), notifies "Linked 2 tasks into ADMIN", and includes the daily note in Open Note(s) whenever task_links is non-empty.

Preview stack

Preview shows every block Bob will write, in Bob's own order: each item's parent

task_line, authored children, then clip.lines and schedule_log.lines when the response carries them. Task-toggle items instead show the route/block destination, the status transition, the daily-note destination, and the + or − Pomodoro-link line Bob planned. Pomodoro-link items show the route/block destination, the status transition ([ ] → [*], [*] already Next, [/] stays In Progress), the daily-note destination, and the ledger outcome Bob planned (Linked under BUGS, Moved Task Link BUGS → FOCUS (created FOCUS), or Task Link already in BUGS; no ledger change.), plus the atomic-start session row when the link starts one. Every item with a pomodoro_link_destination also shows a destination row above it (→ GOALS · next up, → running GOALS 0945–1015, → new Pomodoro BOB, with a timer symbol) from the destination role, and a batch that changed today's Pomodoros section shows one plan-budget meter row above the items (Themes 3/3, Links 8/10 capsules — green within the cap, red over — a +1 BOB delta chip whenever the themes meter grew, and orange warning captions). Both rows join the VoiceOver summary.

Close card

A Pomodoro close instead shows its own card whenever pomodoro_close is present: a stop.circle.fill header with the Close NAME title and the monospaced session range (the shortened half tinted with the Pomodoro-session colour), the day-file destination plus a timing chip that is orange when early, green when on time, and secondary when over, a link via row for link closes (Linked bob.md · ^ready into CAPTURE, Moved from SASE) or a plus.circle row for new-task closes, up to six task rows with per-role glyphs, transitions ([*] → [/], [*] deferred, [x] closed, [x]), text with strikethrough on struck rows, trailing locators, and Work Log previews — badges, transitions, and tags never wrap or shrink, and at narrow widths the locator truncates first, then the one-line task title; row titles show wikilink display text ([[path|Alias]] reads Alias), like the pickers — each row shows its typed_work_log entries first, all of them, with the date stripped, a square.and.pencil glyph in the cyan log tint, and primary text, each followed by its typed_work_log_details lines in secondary callout text, indented to the entry text with no glyph and never capped, followed by the remaining work_log entries in secondary text capped at two — then a notes row (1 note stays), a next-session footer row, and an empty state (No Task Links — the session simply closes) when there are no task rows. Numbered rows start with a fixed-width badge — a filled N.circle.fill when the row is listed, an open N.circle otherwise (monospaced digits in a capsule past 50), tinted orange for in progress, green for complete, and secondary for deferred, with unlisted rows dimmed so chosen rows stand out; unnumbered rows keep a clear spacer so text stays aligned, and with no numbered rows the card looks exactly like today's. Completed rows keep the embedded glyph tinted green with struck text and a [*] → [x] transition when the status changed. One caption row under the task rows teaches the syntax before a selection is typed (x1,2 keeps only these in progress · … · x 2 wrote the tests logs work to 2, tinted like the editor spans, with the 2 in the cyan log tint) and shows the outcome summary after (In progress 1, 3 · Complete 2 · Deferred 4, or In progress none for x0); numbered rows never hide under +N more. The accessibility label includes the typed entries with their details after each entry. The footer's primary action becomes Close, the live-preview, preview, and submit status read Would close … / Closed … with started, completed (only when nonzero), and Work Log counts, and a failed dry run clears the card so no stale preview sits beside the error. The error callout shows Bob's message unchanged; a strict plan-budget refusal (code == plan_theme_cap_exceeded) adds the hint line Queue it with ^ or defer with $<N>. While a list dangles on ,/!/~, the card previews the trimmed draft dimmed with a Type a task number after , row, Close is disabled, and Return cannot submit; while a Work Log bullet dangles (x plus - 1), the card previews the rest dimmed with a Type the Work Log entry for task 1 row, Close stays disabled, and Return cannot submit; a valid draft restores the normal card. While a start list dangles on ~/,, the start card previews the trimmed draft dimmed the same way with Start disabled instead.

Start card

A whole-item =/=<X> start instead shows its own card whenever kind is pomodoro_start: a play.circle.fill header with the Start NAME title and the monospaced session range, the day-file destination, then one caption row — the teaching hint before a drop is typed, the drop summary after (Dropped 2, 4, plus · nothing left queued when nothing stays queued), or the pending notice while a list dangles — followed by the queued-task rows merged with dropped rows in lineup order. A restart or swap puts an orange warning banner first on the card (Overrides the running CAPTURE, with the session range, a detail line, and a monospaced x = chip to close it first) and tints the footer's primary Return orange. A = restart keeps the start pink but swaps the header glyph for arrow.clockwise.circle.fill, retitles to Restart NAME / Restarted NAME, reads Would restart CAPTURE 0920-0945 → 0935-1000 (15m) at line 5 in the status, and adds a dim was 0920–0945 caption under the destination. A =#name swap instead uses arrow.left.arrow.right.circle.fill, retitles to Swap in NAME / Swapped in NAME, reads Would swap in BUGS 0920-0945 (takes over CAPTURE) at line 5 for a kept ledger (or the fresh session range otherwise), adds a Takes over capsule next to the title when the swap keeps the running ledger, and adds an arrow.uturn.down demoted row (CAPTURE → first future · keeps 2 Task Links, plus and its notes). Each numbered row gets the close card's number badge (n.circle, filled for dropped rows, capsule above 50) with status glyphs (circle Ready, circle.inset.filled Next, circle.lefthalf.filled In Progress, questionmark.circle other, minus.circle dropped, exclamationmark.triangle unresolved with the warning as help text), task text (wikilink display text, like the pickers) with a truncating note ^id locator; dropped rows render struck and dimmed with a caption joining stays <status> and with N nested line(s). Numbered rows never hide under +N more; rows without numbers (older Bob) render exactly like today's card. A created session shows a small pink New capsule next to the title on a dry run (Created once committed, and the notification reads Started NAME with 0905–0930 (25m) · New session); with no drop typed and a non-empty lineup, a bare =/=<X> start teaches Type ~2 to drop task 2 · #name to start a specific Pomodoro (Type ~1 to drop it · … for one row), with ~N tinted like the drop span and #name like the editor's name span, while a named start teaches only the drop half. An empty lineup keeps the quiet Type #name to start a specific Pomodoro caption, and named starts with an empty lineup show no hint. Dropped rows read "Task 2, …, drops from today" and queued rows "Task 1, …, queued" for VoiceOver. A = that overrides the running session shows the orange banner on the start card instead of a still-running error, and an override commits only after its warning was on screen. The footer's primary action becomes Start (Restart for a restart, Swap for a swap), the live-preview, preview, and submit status read Would start … / Started … with the session and line (plus · drops 2 / · dropped 2 once a drop is typed), Would restart … / Restarted … with the before → after range for a restart, and Would swap in … / Swapped in … with the takeover or fresh range for a swap, and the notification reads Started NAME with the session and queued-task count (counting tasks only, plus · dropped 2), Restarted NAME with the en-dash session, queued count, and · was 0920–0945, or Swapped in NAME with the taken range and the demoted session back to first future. One item stays compact; a batch renders an ordered stack with item count, destination/kind metadata, dividers, and exact previewBlockLines or toggle transition rows. When Bob reports a global destination, preview and the destination detail show one compact shared-scope line (All items → foo.md or All items → foo.md · under ^a-id) and mark item-level deviations as local overrides instead of repeating the shared target on every item. The outer auxiliary detail region owns scrolling, so preview itself never nests another scroll view. The preview card always takes its natural height: the window grows to show it in full up to the screen limit before the auxiliary region scrolls, and the card keeps its last rendered height while a live preview reloads. Continuous live preview passes --no-clip, so it has no clip to show; the explicit Preview button and Capture resolve the clipboard and therefore mirror the full block.

Pomodoro and task blocks

Whenever Bob reports batch-level pomodoro_blocks, every Pomodoro the capture touches, creates, or reports renders once below the item stack as a full block view: a status caption (Running · line 27 with a play.circle.fill glyph, Completed with checkmark.circle.fill, Queued with circle.dashed) plus a pink New capsule on a dry run (Created once committed), then a card with a status rail, a diff gutter (+ green for added lines, • accent for changed lines with the old text on hover, − red for removed lines), indent guides per depth, and the verbatim lines Bob will write — tinted like the editor (pink time range, wikilink colors, checkbox colors) with no truncation. An added line Bob marks as a surviving successor link carries a trailing lock.open.fill badge (Unblocked successor on hover and to VoiceOver). Standard items whose verbatim lines the blocks already cover omit the duplicated stack, and the blocks dim to 0.6 together with the close card while a close-list selection dangles. An older Bob that omits pomodoro_blocks previews exactly as before. PomodoroBlockDesignTests renders the adjust, close, link-move, created-named-start, and chain fixtures to PNG (light/dark, 760/620 pt, scale 2) when BOB_MAC_CAPTURE_RENDER_DIR is set. Whenever Bob reports batch-level task_blocks, every parent task a sub-bullet capture wrote under renders once below the item stack as a full task card, before the Pomodoro blocks: a picker-status caption (In Progress · sase.md · line 1 with the @+ chooser glyph and color) plus a pink New capsule on a dry run (Created once committed), then the shared diff card with task tinting (tertiary list markers, checkbox colors, secondary #tags and [key:: value] fields, indigo trailing ^block-id, wikilink colors, semibold task-line prose) and no truncation. Covered sub-bullet items switch to a compact ↳ Sub-bullet under ^id header (with › SECTION when Bob reports one) and drop the verbatim stack and trailing note line; uncovered items render exactly as before. Blocks above 24 rows fold quiet runs of 4 or more unchanged rows into ⋯ N unchanged lines rows that expand on click (with a Show all lines action); smaller blocks never fold. A single sub-bullet names its parent in the status and summary (Preview → sase.md › ^id), and an older Bob without task_blocks previews exactly as before. TaskBlockDesignTests renders

Deterministic scheduling

The preview path assigns a fixed BOB_PRIORITY_ROLL_SEED for the draft lifecycle so randomized $<N> scheduled dates can be reused by submission. Bob derives item-specific rolls from that seed for batch drafts, and the seed resets only after a successful capture or discard.

Completing tasks with !

Completing tasks with !

A capture item that is exactly !note:block-id completes that existing open task without closing a Pomodoro: the task line closes exactly as x!N would write it, its Task Links retire in today's ledger the way bob task reconcile would retire them, and Blocked dependents recover the way Ctrl+Enter recovers them. Bulk works one ! item per blank-line-separated block; any failure rolls the whole batch back.

The completion preview card shows the struck task ([*] → [x] with Bob's clean task text struck and dimmed), one row per closed embedded subtask, one row per descendant left open, the ledger effect named per entry (Strikes/Struck its Task Link in CAPTURE, with (completed) for completed entries, Moves/Moved its Task Link SASE → CAPTURE, struck, Task Link already in PLAN; drops/dropped the SASE copy, removes/removed empty SASE), and one row per unblocked dependent — with list.bullet.indent, timer, and lock.open.fill/lock.fill fact rows under a green checkmark.circle.fill seal (Already done — nothing to change in gray for the idempotent no-op). A dependent Bob linked into the predecessor's slot renders with the Next tint, a trailing destination capsule (→ FIX, → new BOB · next up), an added ^id caption when Bob minted its block ID, and an inbox capsule when it lives in an inbox file; a recovered dependent renders secondary with a reason caption (Ready · not planned today); a dependent that stays blocked renders muted with lock.fill and a stays Blocked · … caption. An older Bob without text, struck_in, or dropped keeps the count-based ledger wording and the local task-line text. The footer action is Complete and every fact joins the VoiceOver summary. Notifications read Completed: <task> (Completed N tasks for an all-completion batch) with sase.md · unblocked <name> bodies, plus one extra line when anything was linked (🔓 Next in FIX: <task>, 🔓 3 linked → FIX: …, 🔓 3 linked · FIX, SASE) or the breaker fired. Open Note(s) includes the daily note whenever the ledger changed or any successor was linked. An older Bob without task_complete decodes as no completion preview and keeps the standard card; upgrade Bob before using !.

A capture item that is exactly one bare link (alone, one per line in a pasted URL list, or mixed with ordinary tasks in a blank-line-separated batch) queues that link for the reading queue instead of becoming an inbox task. Bob classifies the URL, checks the library without touching the network, and reports one of not_found, in_library, in_intake, clipping, duplicate, legacy, or unknown; a real run stages a durable ref job that a background worker clips, while a dry run previews the same card without staging anything.

The reference preview card shows the display URL (or the library path when the link is already known), Bob's own detail line (new to your library · clips in the background for a new link, Captured Post · queued for a library hit, waiting for bob ref scan for an intake hit), an Article/PDF/arXiv chip (or the reading-state chip in the library), and — on queued items — the inbox fallback hint naming where the task goes if clipping fails. The footer action is Queue for a queued single link and Done for an already-known one; batches keep Capture. The editor underlines the whole link in the link color via the ref_url span. Notifications read Queued for reading: <link> (or Already in your library: <title>), and Control-Return opens the note or intake PDF only for in_library and in_intake, using Bob's landing data; a queued link opens nothing, because Bob reports no target for it. Command-Return is the editor alias. An older Bob without the ref object keeps capturing the link as an ordinary task with the standard card; upgrade Bob before queueing links. Queueing can be turned off per draft with bob capture -R/--no-ref.

Queueing can be turned off per draft with bob capture -R/--no-ref. The queued job itself is filed by bob in the background; the app never touches the network.

Successor offers

When a current Bob preview reports successor offers for ! completion or x close, Return opens a grouped choice card before the capture is written. Nothing is marked initially, including a single offer. Return is Pull In: it chooses marked offers or, when none are marked, the highlighted offer. Up/Down or Control-N/Control-P moves the highlight, including from the filter. Space marks or unmarks the highlighted offer whenever the filter is empty or unfocused, including immediately after the card opens. The filter takes spaces only once it holds text. Command-A marks every offer, Shift-Return is Skip: it commits with no offers, and Escape returns to the unchanged draft without writing. Control-Return confirms with Return's selection rule, captures, and opens Bob's reported notes. The card shows Bob's global offer numbers, destinations, project folds, blocked residents, and a projected plan-link count when Bob supplied the budget. It is a full-width card like the task picker, with a context line naming what unblocked the offers, destination capsules that turn green on the offers Return will pull in, a live Links projection when Bob supplied the budget, and footer hints with Skip and Pull In N buttons that also work by pointer, plus a lowest-priority ⌃↩ Pull In & Open hint. When the card already carries an open intent (Control-Return from a picker, or Control-Return on the card itself), that hint is omitted and both buttons show a trailing arrow.up.forward glyph. The success notification reports what was pulled in.

Inbox routing

When a matching Bob preview reports that a capture will change the status of an existing inbox task, the preview adds an Inbox · … · ↩ choose where it goes row. Return opens the route card before writing, after the successor choice when both choices are needed. The card shows each changed task and Bob's available areas and open projects, with the destination path, parent, project status, and Blocked mark. Filter the list locally, then use Return to move the task or Shift-Return to keep it in its inbox. Control-Return moves it and opens the capture notes. With multiple tasks, answer them one at a time; the previous destination is highlighted when it remains available.

Escape goes back one task. From the first task it returns to the earlier successor card, when present, or to the unchanged draft. If Bob refuses a move, the card reopens on that task with Bob's reason. If the task changed since the preview, the card closes and Return asks Bob for a fresh choice. The success notice says which destination was used or that the task stayed in its inbox.

Relationship rows

Preview shows one ⛓ depends on · Buy shoes · foo.md ^bar · Ready → In Progress or 🧱 blocks · Do cleanup tasks · job.md ^cleanup · Next → Blocked row per relationship, in Bob's dependency_updates[] order. Already-present rows say already depends on or already blocks; project-resident counts remain on the block row. Managed Depends-On/Blocks Markdown is omitted from task cards while these rows are present. A task_dependency item uses an owner header with its text, note and block ID, and resulting status; the owner task card appears before related task cards, and its repeated task line is omitted when the card covers it. Success notifications name the owner and relationship, use the route label as their subtitle, and include consumed-link detail when Bob reports one. Consumed links use Bob's session name, time, state, action, and removal flag; deleted sessions render red, and removed Pomodoro lines keep the red diff style. A dependency-only action is never labeled "Create task". The & and * editor spans are supplied by Bob; Swift never scans either sigil to parse capture grammar. Older Bob payloads without resolved relationship details keep their legacy preview and notification copy.

Relation rows (~ / ~~)

Preview shows one 🪢 links to "Design a retry policy" · see sase.md ^retry-design or 🪢 "Ship the sase blog" now links here · next step sase_blog.md ^prj row per edge, in Bob's relation_updates[] order. Relabeled rows say 🪢 relabeled "Retry design" · next; already-linked rows say 🪢 already linked "Retry design". An archived target appends the muted · archived: LINKED FROM shows live only suffix because the courtesy mirror is skipped. Managed **LINKS:** / **LINKED FROM:** Markdown is omitted from task cards while relation rows are present. Relation rows never render red, consumption, or status arrows. A || dry run that would hide a visible label renders Bob's hides_existing_relation_label message as an amber row; the commit appends · hid "label" from relation suggestions to the notification. Success notifications count relations separately: a single ~ reads 🪢 owner now links to "target", a single ~~ reads 🪢 "source" now links here, and larger or mixed batches read owner · depends on D · links R. New relation IDs render as 🪢 New relation ID · path ^id (source|target) captions. The ~ editor spans are supplied by Bob; Swift never scans the sigil to parse capture grammar. When the draft contains ~ but the parse response lacks relation_grammar, the panel shows the calm This bob predates ~ links; ~ text would be captured literally · update bob warning: the first ⌘↩ only shows the warning, the second ⌘↩ submits.

Similar tasks

When a capture draft will create a new task, the preview card may grow a Similar tasks band at its foot — after the destination line, relationship rows, blocks, and every other capture consequence, never displacing them. The rows come from bob capture-related after typing settles; the app only schedules the call and paints what Bob returned. A project-note title uses the same band on its card.

The band takes zero pixels unless a match clears Bob's bar: no header, no placeholder, and never a "No duplicates" line. Percentages, scores, spinners, and empty states are never shown. Held rows stay put while you type and swap only when the settled result changed, with a 120 ms cross-fade (instant with Reduce Motion) and no height animation.

A single-item card shows a hairline, a SIMILAR TASKS header with an optional +N capsule and a trailing ⌘1–2 hint, then at most two rows. Each row is one line: a ⌘N keycap, the status glyph, the title with matched words semibold and tail-truncated, an optional project folder or reading book glyph, at most one capsule, then route · Status. The locator truncates first, then the title; capsules never wrap or shrink. Capsules are Same title and Possible duplicate (warning tint), Done 8d ago / Canceled 8d ago (secondary on quaternary), or Related / Prerequisite / Blocked by this after you act. Closed titles are secondary and never struck through.

A bulk draft puts at most one row on each task-creating card, with a leading ⧉ mark and no header; numbering continues down the stack. If the band would make the auxiliary region scroll, every band folds to one line before anything else moves, for example ⧉ 2 similar tasks · Possible duplicate: Install new UPS battery ⌘1.

Each row is one VoiceOver element, labelled <title>, <capsule>, <route>, <status or closed text>. One announcement fires per settled non-empty change that differs from the last; typing never announces, and focus never moves to the band. Hover shows the full title and note path without changing layout. Setting Show similar tasks while capturing (on by default) hides the band and stops the related lane.

Pressing ⌘1–⌘5 focuses that row and opens its action strip on a second line; pressing the same key again unfocuses it. In a folded band, ⌘1 shows that row and its strip until unfocused. The strip lists only the actions Bob offered, always in the order ~ + & * / ⌫, with the default preselected. The footer swaps to key hints while a row is focused, naming the default so the meaning of Return stays visible.

Action Keys Draft edit
Relate ~ Appends Bob's relation_to at the item anchor
Add as note + Replaces the item's route with Bob's parent, or appends it when there is no route
Needs it & Appends Bob's dependency at the item anchor
Blocks it * Appends Bob's blocked_task at the item anchor
Open / Opens the task in Obsidian, keeping the draft
Discard mine ⌫ Deletes the item's range

Defaults come from Bob: similar rows default to Relate, closed rows default to Open, possible_duplicate rows default to Add as note, and same_title rows default to Discard mine (or Add as note when the item has authored children). Every splice is undoable through the native text view, then runs the live preview again; ⌘Z restores a discarded item. Focusing a held row after more typing fires the related lane at once and applies the chosen action against the fresh anchors. When the task is gone, the band announces "Similar tasks changed" and unfocuses. Open keeps the draft and hides the panel. Return is never intercepted: with no row focused it captures as today.

Each focused row exposes one VoiceOver custom action per offered strip action; the strip itself is labelled with the row, the default, and the available actions.

Rows for tasks that still need a block ID offer the same strip, but choosing a link action opens the Add block ID prompt prefilled with Bob's first suggestion (Tab cycles them). Submitting writes the ID now, before Return, and splices the matching ~ / @+ / & / * replacement; Escape cancels with no write and keeps the band focus. Open and Discard mine need no ID.

↓ on the last visible row of an item reveals that item's next hidden match, up to Bob's limit. Unfocusing the band collapses the reveal back to the capped rows.

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