# Orkmode 9P Namespace Design v2 ## Structure ``` ork/ ├── ctl # rdwr: "reload", "quit", "rescan" ├── idx # read: list of org files (name\tpath\ttitle) ├── keywords # read: configured TODO keywords ├── new # rdwr: create org file, returns filename │ ├── agenda/ # computed views │ ├── today # read: items scheduled/deadline today │ ├── week # read: items this week │ └── todos # read: all open TODOs across files │ └── {doc}/ # e.g., "todo" for todo.org ├── raw # read/write: full org file content ├── body # read/write: full org file content (alias) ├── meta # read: title, author, filetags (tsv) ├── new # rdwr: create section, returns UUID │ └── {uuid}/ # section by UUID ├── headline # read: full headline "* TODO [#A] Title :tag:" ├── title # read/write: plain title text ├── keyword # read/write: TODO/DONE/etc or empty ├── priority # read/write: A/B/C or empty ├── tags # read/write: colon-separated "work:urgent" ├── level # read: headline level (1-6) ├── scheduled # read/write: "<2025-01-15 Wed>" ├── deadline # read/write: "<2025-01-20 Mon>" ├── closed # read/write: "<2025-01-21 Tue>" ├── properties # read/write: "KEY=value\n" lines ├── body # read/write: section content (not children) ├── new # rdwr: create child section, returns UUID │ └── {child-uuid}/ # nested children (same structure) └── ... ``` ## Operations by File | Path | Mode | Read | Write | Notes | |------|------|------|-------|-------| | `ctl` | rdwr | — | command | "reload" rescans, "quit" exits | | `idx` | r | file list | — | `name\tpath\ttitle` per line | | `keywords` | r | keyword config | — | `TODO NEXT \| DONE` | | `capture` | w | — | org text | Appends to inbox.org | | `agenda/*` | r | computed | — | Filtered section lists | | `{doc}/raw` | rw | full content | replace all | Escape hatch | | `{doc}/meta` | r | metadata | — | | | `{doc}/{id}/headline` | r | full line | — | Reconstructed | | `{doc}/{id}/title` | rw | plain text | update | | | `{doc}/{id}/keyword` | rw | keyword | update | | | `{doc}/{id}/priority` | rw | A/B/C/empty | update | | | `{doc}/{id}/tags` | rw | colon-sep | replace | Write "work:urgent" | | `{doc}/{id}/scheduled` | rw | timestamp | update | Write "<2025-01-15>" | | `{doc}/{id}/deadline` | rw | timestamp | update | | | `{doc}/{id}/properties` | rw | key=val lines | replace | | | `{doc}/{id}/body` | rw | content | replace | Text between headline and children | ## ID Generation 1. `CUSTOM_ID` property if present 2. Else `ID` property if present 3. Else slugified title + `-` + short hash of position IDs are stable unless CUSTOM_ID/ID changes or section is deleted. ## Nested Hierarchy The hierarchy mirrors org structure: ``` todo/ ├── project-alpha/ # * TODO Project Alpha │ ├── title # "Project Alpha" │ ├── keyword # "TODO" │ ├── design-doc/ # ** TODO Design doc │ │ ├── title │ │ └── review/ # *** Review │ │ └── title │ └── implementation/ # ** Implementation │ └── title └── daily-standup/ # * TODO Daily standup └── title ``` Listing `todo/project-alpha/` returns children: `design-doc`, `implementation` (plus property files). ## Write Semantics **Simple properties** (title, keyword, priority): - Write replaces value - Server updates .org file atomically **Tags**: - Write replaces all tags: `echo "work:urgent" > tags` - To add: read, append, write back (client responsibility) - Or: future `ctl` commands: `echo "tag +urgent" > ctl` **Timestamps** (scheduled, deadline): - Write org timestamp format: `<2025-01-15 Wed>` - Or simplified: `2025-01-15` (server adds day name) - Write empty to remove **Body**: - Write replaces section body content - Does NOT affect children (only text between headline and first child) **Properties**: - Write replaces entire drawer - Format: `KEY=value\n` lines ## EDSL Example (Rust) Porting ollie's virtfs pattern: ```rust fn build_namespace(state: &OrkState) -> FsNode { dir("/", file("ctl", 0o222, rdwr(|data| state.handle_ctl(data))), file("idx", 0o444, read(|| state.list_docs())), file("keywords", 0o444, read(|| state.keywords())), file("capture", 0o222, write(|data| state.capture(data))), dir("agenda", file("today", 0o444, read(|| state.agenda_today())), file("week", 0o444, read(|| state.agenda_week())), file("todos", 0o444, read(|| state.all_todos())), ), each("{doc}", || state.doc_names(), |doc| { dir(doc, file("raw", 0o644, read(|| state.doc_raw(doc)), write(|data| state.write_doc_raw(doc, data))), file("meta", 0o444, read(|| state.doc_meta(doc))), each("{section}", || state.section_ids(doc), |id| { section_node(state, doc, id) }), ) }), ) } fn section_node(state: &OrkState, doc: &str, id: &str) -> FsNode { dir(id, file("headline", 0o444, read(|| state.headline(doc, id))), file("title", 0o644, read(|| state.title(doc, id)), write(|data| state.set_title(doc, id, data))), file("keyword", 0o644, read(|| state.keyword(doc, id)), write(|data| state.set_keyword(doc, id, data))), // ... etc // Nested children each("{child}", || state.child_ids(doc, id), |child_id| { section_node(state, doc, child_id) }), ) } ``` ## Questions Resolved 1. **Hierarchy**: Nested directories reflecting org structure ✓ 2. **IDs**: CUSTOM_ID > ID > slug+hash ✓ 3. **Operations**: Read/write per property, rdwr for ctl ✓ 4. **Tags**: Simple replace (add/remove via read-modify-write or future ctl) 5. **Body**: Section content only, not children ✓ ## Not Needed (for v1) - `statewait` / blocking reads — GUI can poll or use inotify on .org files - `search` file — agenda views sufficient for now - Symlinks — just return data directly ## Implementation Order 1. Port virtfs EDSL to Rust 2. Build 9P server using `nine` crate 3. Implement read-only namespace first 4. Add write operations 5. File watching for external changes