Personal outliner built with Rust.
3

Configure Feed

Select the types of activity you want to include in your feed.

archive trawler-mvp: sync delta specs to openspec/specs, move change to archive/2026-07-12-trawler-mvp

graham.systems (Jul 12, 2026, 2:52 PM -0700) f262b286 7c8ce01c

+294
openspec/changes/trawler-mvp/.openspec.yaml openspec/changes/archive/2026-07-12-trawler-mvp/.openspec.yaml
openspec/changes/trawler-mvp/README.md openspec/changes/archive/2026-07-12-trawler-mvp/README.md
openspec/changes/trawler-mvp/design.md openspec/changes/archive/2026-07-12-trawler-mvp/design.md
openspec/changes/trawler-mvp/proposal.md openspec/changes/archive/2026-07-12-trawler-mvp/proposal.md
openspec/changes/trawler-mvp/specs/block-graph/spec.md openspec/changes/archive/2026-07-12-trawler-mvp/specs/block-graph/spec.md
openspec/changes/trawler-mvp/specs/graph-navigation/spec.md openspec/changes/archive/2026-07-12-trawler-mvp/specs/graph-navigation/spec.md
openspec/changes/trawler-mvp/specs/journal/spec.md openspec/changes/archive/2026-07-12-trawler-mvp/specs/journal/spec.md
openspec/changes/trawler-mvp/specs/outline-editor/spec.md openspec/changes/archive/2026-07-12-trawler-mvp/specs/outline-editor/spec.md
openspec/changes/trawler-mvp/specs/search/spec.md openspec/changes/archive/2026-07-12-trawler-mvp/specs/search/spec.md
openspec/changes/trawler-mvp/specs/steel-queries/spec.md openspec/changes/archive/2026-07-12-trawler-mvp/specs/steel-queries/spec.md
openspec/changes/trawler-mvp/tasks.md openspec/changes/archive/2026-07-12-trawler-mvp/tasks.md
+60
openspec/specs/block-graph/spec.md
··· 1 + # block-graph Specification 2 + 3 + ## Purpose 4 + 5 + The core data model: blocks, outline hierarchy, pages, references, tags, and properties, with Loro-backed persistence and derived in-memory indexes. 6 + 7 + ## Requirements 8 + 9 + ### Requirement: Everything is a node 10 + The system SHALL represent blocks, pages, tags, and journal dates as nodes in a single graph. Pages SHALL be nodes that own an ordered tree of block nodes; tags and dates SHALL be referenceable nodes with no special-cased storage. 11 + 12 + #### Scenario: A tag is navigable like a page 13 + - **WHEN** a block is tagged `#project` 14 + - **THEN** `project` exists as a node whose backlinks include that block, exactly as a `[[project]]` page reference would 15 + 16 + ### Requirement: Stable block identity 17 + Every block SHALL have a globally unique, stable identifier that survives edits, moves, reparenting, application restarts, and (future) sync merges. 18 + 19 + #### Scenario: Identity survives restructuring 20 + - **WHEN** a block is edited, indented under a new parent, and the application is restarted 21 + - **THEN** the block retains its original identifier and all references to it resolve 22 + 23 + ### Requirement: Outline tree operations 24 + The system SHALL support creating, deleting, splitting, and merging blocks, and moving blocks (with their subtrees) to any position under any parent, preserving sibling order. 25 + 26 + #### Scenario: Subtree move 27 + - **WHEN** a block with three descendant blocks is moved under a different parent 28 + - **THEN** the block and all three descendants appear at the new location in their original relative order, and the source location no longer contains them 29 + 30 + ### Requirement: Loro document is the source of truth 31 + The system SHALL persist the graph exclusively as a Loro CRDT document (movable tree for hierarchy, text for block content, maps for metadata), stored as a snapshot plus incremental update blobs in the graph directory. No derived structure may hold data that cannot be rebuilt from the Loro document. 32 + 33 + #### Scenario: Derived indexes are disposable 34 + - **WHEN** all derived index files are deleted and the application is restarted 35 + - **THEN** the graph loads completely from the Loro document and all queries, backlinks, and search results are identical to their pre-deletion state 36 + 37 + #### Scenario: Crash safety 38 + - **WHEN** the process is killed immediately after an edit is acknowledged in the UI 39 + - **THEN** on next startup the edit is present 40 + 41 + ### Requirement: Block references and backlinks 42 + Block content SHALL support inline references to pages, tags, dates, and individual blocks. The system SHALL maintain a backlink index mapping every node to the set of blocks that reference it, updated within the same edit transaction. 43 + 44 + #### Scenario: Backlink appears immediately 45 + - **WHEN** the user types a reference to `[[fishing]]` inside a block and confirms it 46 + - **THEN** the `fishing` node's backlink set includes the referencing block without an application restart or manual reindex 47 + 48 + ### Requirement: Lightweight properties 49 + Blocks SHALL support key–value properties where values are typed as text, number, date, boolean, or node reference. Property keys SHALL be queryable across the graph. Property schemas, classes, and inheritance are explicitly out of scope for this change. 50 + 51 + #### Scenario: Property round-trip 52 + - **WHEN** a block is given the property `due` with the date value `2026-07-10` 53 + - **THEN** a query for blocks with a `due` property returns the block, and the value is retrieved as a date (not a string) 54 + 55 + ### Requirement: Incremental index maintenance 56 + The in-memory graph and all derived indexes SHALL be updated incrementally on every edit, and an incremental state SHALL be behaviorally indistinguishable from a full rebuild. 57 + 58 + #### Scenario: Incremental equals rebuild 59 + - **WHEN** a randomized sequence of at least 1,000 edit operations (create, edit, move, delete, tag, reference) is applied 60 + - **THEN** the incrementally-maintained indexes are equal to indexes rebuilt from scratch from the Loro document
+42
openspec/specs/graph-navigation/spec.md
··· 1 + # graph-navigation Specification 2 + 3 + ## Purpose 4 + 5 + Moving through the graph: following references, backlinks, block zoom, and keyboard hopping between linked nodes. 6 + 7 + ## Requirements 8 + 9 + ### Requirement: Follow reference from keyboard 10 + The system SHALL provide a keyboard action that follows the reference at or nearest the cursor in the focused block, navigating to the referenced node ("go to definition" for thoughts). 11 + 12 + #### Scenario: Hop to a referenced page 13 + - **WHEN** the cursor is on a `[[nets]]` reference and the user invokes follow-reference 14 + - **THEN** the view navigates to the `nets` page with focus in its first block 15 + 16 + ### Requirement: Backlinks are always visible 17 + When viewing any node (page or zoomed block), the system SHALL display the blocks that reference it, grouped by their containing page, each rendered with enough surrounding context to be intelligible and navigable to its source. 18 + 19 + #### Scenario: Backlink panel navigation 20 + - **WHEN** the user selects an entry in the backlinks section 21 + - **THEN** the view navigates to that block in its home page with the block focused 22 + 23 + ### Requirement: Block zoom 24 + The system SHALL allow zooming into any block, making it the temporary root of the view (showing only its subtree plus a breadcrumb of ancestors), and zooming back out. 25 + 26 + #### Scenario: Zoom in and out 27 + - **WHEN** the user zooms into a block three levels deep 28 + - **THEN** the view shows only that block's subtree with an ancestor breadcrumb, and invoking zoom-out (or a breadcrumb entry) restores the wider view 29 + 30 + ### Requirement: Navigation history 31 + The system SHALL maintain a navigation history with keyboard-accessible back and forward actions covering page visits, zooms, and reference hops. 32 + 33 + #### Scenario: Retrace a hop chain 34 + - **WHEN** the user follows references across three pages and invokes back three times 35 + - **THEN** the view returns through the visited locations in reverse order, restoring scroll and focus position at each step 36 + 37 + ### Requirement: Quick open 38 + The system SHALL provide a keyboard-invoked switcher that fuzzy-matches page and tag names (including journal dates) and navigates to the selection. 39 + 40 + #### Scenario: Jump by name fragment 41 + - **WHEN** the user invokes quick open and types `trawl` 42 + - **THEN** nodes whose names fuzzy-match (e.g., `trawler-design`) are listed, and confirming navigates to the selected node
+32
openspec/specs/journal/spec.md
··· 1 + # journal Specification 2 + 3 + ## Purpose 4 + 5 + Daily notes: automatic date pages, date references, and the journal timeline as the application's home view. 6 + 7 + ## Requirements 8 + 9 + ### Requirement: Automatic daily pages 10 + The system SHALL ensure a journal page exists for the current local date whenever the application opens or the date rolls over while running. Journal pages SHALL be ordinary page nodes distinguished by a date attribute, created lazily and never duplicated. 11 + 12 + #### Scenario: First launch of the day 13 + - **WHEN** the application opens on a date with no existing journal page 14 + - **THEN** a journal page for that date exists and is focused with an empty first block ready for input 15 + 16 + #### Scenario: No empty-page litter 17 + - **WHEN** a journal page was auto-created but never received content 18 + - **THEN** it is not shown in the timeline on subsequent days 19 + 20 + ### Requirement: Journal timeline is the home view 21 + The application SHALL open to a chronological timeline with today's journal page at the top and previous non-empty journal pages loading below it as the user scrolls. 22 + 23 + #### Scenario: Opening the app 24 + - **WHEN** the application starts 25 + - **THEN** today's journal page is displayed with focus in its first block, and scrolling down reveals prior days in reverse-chronological order 26 + 27 + ### Requirement: Date references resolve to journal pages 28 + Date references in block content SHALL be node references to the corresponding journal page, participating in backlinks like any other reference. Typing support SHALL make date references cheap to produce (e.g., a date picker or natural-language completion). 29 + 30 + #### Scenario: Scheduling by reference 31 + - **WHEN** a block on any page references `[[2026-07-10]]` 32 + - **THEN** the journal page for 2026-07-10 lists that block in its backlinks, visible when that day is viewed
+58
openspec/specs/outline-editor/spec.md
··· 1 + # outline-editor Specification 2 + 3 + ## Purpose 4 + 5 + The GPUI editing surface: one-hot block editing, keyboard-first outline manipulation, and rendered display of inactive blocks. 6 + 7 + ## Requirements 8 + 9 + ### Requirement: One-hot block editing 10 + The application SHALL maintain at most one live text input at any time — the focused block. All other blocks SHALL be rendered as read-only styled views. Moving focus SHALL commit the current block's content, re-render it, and attach the editor to the target block. 11 + 12 + #### Scenario: Focus transition commits content 13 + - **WHEN** the user edits a block and presses Down to focus the next block 14 + - **THEN** the edited block displays its new rendered content and the next block becomes the sole editable input, with no intermediate state where zero or two editors exist 15 + 16 + ### Requirement: Keyboard-complete outline manipulation 17 + All outline operations SHALL be executable without the mouse: create sibling (Enter), insert a newline within the block (Shift+Enter), split block at cursor, indent/outdent (Tab/Shift+Tab), move block up/down among siblings, delete/merge with previous (Backspace at start), and fold/unfold subtree. 18 + 19 + #### Scenario: Indent under previous sibling 20 + - **WHEN** the cursor is in a block and the user presses Tab 21 + - **THEN** the block (with its subtree) becomes the last child of its previous sibling, and the cursor position within the text is preserved 22 + 23 + #### Scenario: Newline within a block vs. new block 24 + - **WHEN** the user presses Shift+Enter mid-block, types a second paragraph, then presses Enter 25 + - **THEN** the block contains both paragraphs, and a new empty sibling block is created and focused 26 + 27 + #### Scenario: Fold hides descendants 28 + - **WHEN** the user folds a block with descendants 29 + - **THEN** descendants are hidden, a fold indicator is shown, and keyboard navigation skips the hidden blocks 30 + 31 + ### Requirement: Markdown rendering of block content 32 + Inactive blocks SHALL render their content as full Markdown — multiple paragraphs, emphasis, code spans, fenced code blocks, quotes, embedded lists, and links — plus node references as an inline extension. Node references SHALL be visually distinct, SHALL identify their target, and SHALL NOT be parsed inside code spans or fenced code blocks. Markdown structure within a block is content only: it SHALL NOT create outline nodes. 33 + 34 + #### Scenario: Reference renders as a link 35 + - **WHEN** a block containing `[[gear-maintenance]]` is not focused 36 + - **THEN** the reference renders as a styled link displaying the target page's name 37 + 38 + #### Scenario: Multi-paragraph block renders fully 39 + - **WHEN** a block containing two paragraphs and a fenced code block is not focused 40 + - **THEN** both paragraphs and the syntax-styled code fence render within the single block, which folds, moves, and is referenced as one node 41 + 42 + #### Scenario: References in code are literal 43 + - **WHEN** a block's fenced code block contains the text `[[not-a-link]]` 44 + - **THEN** it renders as literal code, creates no backlink, and `not-a-link` gains no referencing node 45 + 46 + ### Requirement: Editing latency 47 + Focus transitions between blocks SHALL complete within one frame at 60Hz (under ~16ms) on the development machine, measured from keypress to the new editor accepting input, for outlines of at least 10,000 visible-tree blocks. 48 + 49 + #### Scenario: Rapid navigation stays responsive 50 + - **WHEN** the user holds Down to traverse 50 consecutive blocks 51 + - **THEN** focus tracks the keypress rate with no perceptible lag or dropped keystrokes 52 + 53 + ### Requirement: Large outlines render lazily 54 + The outline view SHALL virtualize rendering such that memory and frame time scale with visible blocks, not total blocks in the page. 55 + 56 + #### Scenario: Long journal page scrolls smoothly 57 + - **WHEN** a page contains 10,000 blocks and the user scrolls through it 58 + - **THEN** scrolling maintains 60fps and only visible blocks are materialized as UI elements
+42
openspec/specs/search/spec.md
··· 1 + # search Specification 2 + 3 + ## Purpose 4 + 5 + Full-text search and lexical similar-content suggestions over the block graph. 6 + 7 + ## Requirements 8 + 9 + ### Requirement: Full-text search over all blocks 10 + The system SHALL index the text content of every block (tantivy) and provide keyboard-invoked search returning matching blocks ranked by relevance, with match highlighting and navigation to the source block. 11 + 12 + #### Scenario: Search and jump 13 + - **WHEN** the user invokes search and types `otter trawl` 14 + - **THEN** blocks containing the terms are listed ranked by relevance with matches highlighted, and confirming an entry navigates to that block in its page 15 + 16 + ### Requirement: Index freshness 17 + Newly created or edited block content SHALL be searchable within one second of the edit, without manual reindexing. 18 + 19 + #### Scenario: Just-typed content is findable 20 + - **WHEN** the user writes a block containing a novel term and invokes search for it within a second 21 + - **THEN** the block appears in the results 22 + 23 + ### Requirement: Search is available to queries 24 + Full-text search SHALL be exposed as a set-returning primitive in the Steel query API, composable with other set primitives. 25 + 26 + #### Scenario: Search composed with a tag filter 27 + - **WHEN** a query intersects full-text results for `"bycatch"` with the `#research` tag set 28 + - **THEN** only blocks matching both are returned 29 + 30 + ### Requirement: Similar blocks (lexical) 31 + The system SHALL suggest blocks lexically similar to a given block (shared distinctive terms, more-like-this style), excluding the block itself and its ancestors/descendants, ranked by similarity. The similarity provider SHALL be a swappable interface so a semantic (embedding) tier can be added later without changing consumers. 32 + 33 + #### Scenario: Related note resurfaces 34 + - **WHEN** the user views similar blocks for a block about "mending the cod-end after the last haul" 35 + - **THEN** previously written blocks sharing distinctive vocabulary (e.g., other cod-end or net-repair notes) are listed and navigable, and unrelated blocks are not 36 + 37 + ### Requirement: Search index is disposable 38 + The search index SHALL be entirely derivable from the block graph. Deleting the index directory SHALL trigger a transparent rebuild on next startup with no data loss. 39 + 40 + #### Scenario: Rebuild after deletion 41 + - **WHEN** the search index directory is deleted and the application restarts 42 + - **THEN** search returns the same results as before deletion once the rebuild completes
+60
openspec/specs/steel-queries/spec.md
··· 1 + # steel-queries Specification 2 + 3 + ## Purpose 4 + 5 + The Steel (Scheme) query integration: read-only query API, query blocks with live previews, and the in-app Lisp editing experience. 6 + 7 + ## Requirements 8 + 9 + ### Requirement: Query blocks live in the outline 10 + The system SHALL support a query block: an outline block whose content is a Steel expression and which renders its evaluation result inline beneath the expression. Query blocks SHALL be ordinary blocks (movable, referenceable, foldable). 11 + 12 + #### Scenario: Results render in place 13 + - **WHEN** a query block contains `(blocks (and (tag 'project) (ref "rust")))` 14 + - **THEN** the matching blocks render beneath the query as a navigable outline slice, each entry linking to its source block 15 + 16 + ### Requirement: Read-only capability scoping 17 + Query evaluation SHALL run in a Steel context where only read-only graph primitives are registered. No mutation of the graph, filesystem access, or network access SHALL be reachable from a query. 18 + 19 + #### Scenario: Mutation is unavailable 20 + - **WHEN** a query attempts to call any mutating or I/O operation 21 + - **THEN** evaluation fails with an unknown-identifier error and the graph is unchanged 22 + 23 + ### Requirement: Set-algebra query primitives 24 + The query API SHALL expose native set-returning primitives — at minimum: blocks by tag, blocks referencing a node, blocks with a property (optionally matching a value), blocks within a date range, descendants of a node, and full-text search results — plus native `and`/`or`/`not` combinators that operate on sets without per-block Steel callbacks. 25 + 26 + #### Scenario: Native combination stays fast 27 + - **WHEN** a query intersects a 20,000-block tag set with a 5,000-block date-range set on a 100,000-block graph 28 + - **THEN** evaluation completes within 50ms, with the intersection performed natively rather than by iterating blocks in Scheme 29 + 30 + #### Scenario: Scheme predicates over narrowed sets 31 + - **WHEN** a query applies a Steel lambda filter to a set already narrowed to under 1,000 blocks 32 + - **THEN** the per-block predicate is applied and results are correct 33 + 34 + ### Requirement: Live preview with debounce 35 + While a query block is being edited, the system SHALL re-evaluate it on a debounce and update the rendered results without requiring the user to leave the block. 36 + 37 + #### Scenario: Editing refines results live 38 + - **WHEN** the user extends `(tag 'project)` to `(and (tag 'project) (since "2026-06"))` and pauses typing 39 + - **THEN** the preview updates to the narrowed result set without explicit re-run 40 + 41 + ### Requirement: Runaway query containment 42 + Query evaluation SHALL run off the UI thread and SHALL be cancellable. A query exceeding its time budget SHALL be terminated and reported as timed out; the UI SHALL remain responsive throughout. 43 + 44 + #### Scenario: Infinite loop cannot freeze the app 45 + - **WHEN** a query block contains a non-terminating expression 46 + - **THEN** the UI continues to respond, the evaluation is terminated at the time budget, and the query block displays a timeout error 47 + 48 + ### Requirement: Lisp editing experience 49 + When editing a query block, the editor SHALL provide Scheme syntax highlighting (tree-sitter), matching-paren indication, and inline display of evaluation errors with position information where available. 50 + 51 + #### Scenario: Error is shown at the block 52 + - **WHEN** a query contains an unbalanced paren or unknown identifier 53 + - **THEN** the query block displays the error message inline instead of results, and the rest of the outline is unaffected 54 + 55 + ### Requirement: Table rendering for property queries 56 + Query results SHALL render as an outline slice by default, and as a table when the query projects named properties. 57 + 58 + #### Scenario: Projected properties become columns 59 + - **WHEN** a query projects each matching block's content and its `due` property 60 + - **THEN** results render as a two-column table with one row per block, sortable by column