diff options
Diffstat (limited to 'docs/superpowers/specs/2026-10-16-notes-design.md')
| -rw-r--r-- | docs/superpowers/specs/2026-10-16-notes-design.md | 29 |
1 files changed, 19 insertions, 10 deletions
diff --git a/docs/superpowers/specs/2026-10-16-notes-design.md b/docs/superpowers/specs/2026-10-16-notes-design.md index 459b526..b8524a2 100644 --- a/docs/superpowers/specs/2026-10-16-notes-design.md +++ b/docs/superpowers/specs/2026-10-16-notes-design.md @@ -91,10 +91,12 @@ Opened by a row (existing note) or **New note**. renders the body as Markdown; Edit shows the raw Markdown in a `TextEdit`. An existing note opens in Preview, a new one in Edit. - **Save** is two clicks, so the note is on disk before git is involved: the - first writes it and the button becomes **Commit & push?**; the second runs - the git half (see Git) and returns to the list. Leaving with the second click - unmade keeps the saved note and does not push it. With git off there is no - git half, so Save writes and returns to the list in one click. + first writes it and the button becomes **Commit & push?**; the second writes + whatever the editor holds then, commits and pushes (see Git), and returns to + the list. The editor stays editable between the clicks, so the second has to + write as well: pushing alone would drop an edit made after the first. Leaving + with the second click unmade keeps the saved note and does not push it. With + git off there is no second half, so Save writes and returns in one click. - **Cancel** returns to the list without saving. **Delete**, existing notes only, asks first: the first click arms the button, the second removes the note and pushes the removal. Nothing is destroyed until the second click, a @@ -109,9 +111,15 @@ Opened by a row (existing note) or **New note**. ## Writes All index writes go through the module's `db.json` `FileView` with -`atomicWrites: true`, so a write that dies mid-way cannot leave a half -`db.json` for `notes.sh` to trip on. A body is written with a `FileView` on -its own path, which creates the file. +`atomicWrites: true` and `blockWrites`, so a write that dies mid-way cannot +leave a half `db.json` for `notes.sh` to trip on, and its outcome is known when +the call returns. + +A body is written by a `Process` running `cat > <file>` with the text on stdin, +not by a `FileView`. `setText` with an **empty** string, on a path that view has +not loaded, creates no file at all and reports no error, which put a note with +an empty body into `db.json` with no file beside it. `cat` takes any content at +any length, including none, and its exit code is the outcome. **A write is refused when `db.json` cannot be read as the index.** That is not hypothetical: `git pull --rebase --autostash` resolves a remote `db.json` @@ -122,9 +130,10 @@ and writing it back would delete every title, so the write instead reports git to resolve. `Notes.valid` is the check; `Notes.parse` stays tolerant for display, returning an empty store for the same input. -A write that fails is reported, not assumed to have landed: both writes block -(`blockWrites`), because `setText` is asynchronous otherwise, and a failure -that nobody observes reports a save that never happened. +A write that fails is reported, not assumed to have landed: the index write +blocks (`blockWrites`) and the body write's `cat` exit code is checked, because +`setText` is asynchronous otherwise and a failure nobody observes reports a +save that never happened. - **Create**: the first click pulls first (see Git), then re-reads `db.json` so the append is based on what the pull brought in, not on what was read |
