Repository navigation
Conversation
bounded_body redacts the token from an upstream error body with str::replace, and an empty pattern matches between every character, so with no token every detail came back with "[REDACTED]" between each letter. An empty token is now left alone. Config never builds one in production, but the function no longer depends on that.
put_notes measured a workspace's note list before checking whether the cache keeps anything, so with a zero TTL, which the tests use, it walked every note only to throw the size away.
RemoteSource repeated NoteSource without tracked, only so list_notes could hand remote sources to list_remote. list_notes now matches on NoteSource itself. Tracked records still skip the limit check, which their own listing makes, and each remote source keeps its default order and account-wide rules.
HackMD reads \r\n and a lone \r back as \n (measured 2026-10-08), so a body sent with CRLF could never be confirmed by its read-back, and a working file saved with CRLF looked changed on every check. Bodies a caller supplies (content, create, a patch result) and every local file read for sync are converted first. A body read from HackMD and resent goes back exactly as read. An explicit title always wins and later body edits never rename a note, so an edited H1 leaves the listing behind. Pull and push now report title_drift, the title the body gives, when it differs from the listed one. It is read only from plain front matter or an H1 that is the body's first line of text; anything else, including YAML or Markdown the parser does not decode, gives none, since a wrong suggestion would rename a note. Read-back now checks an explicit title too, which the old precedence notes had excluded. Pull also reports changes, a bounded diff of what it replaced in the local file. LocalFiles::write_atomic takes an Expect, the size and modification time of the file the caller read, or Absent, checked just before the rename, so an editor's save during a pull or under a conflict's *.remote.md is refused as a conflict instead of lost. A snapshot an editor only resaved with CRLF still counts as the server's.
Adding stamp left read_stamped's description above it, so stamp's doc opened with a byte limit and a file handle it does not have, and read_stamped had none. Each now carries its own.
A write that expects no file refuses a dangling symlink, but the message said the file changed after it was read and to read it again, when nothing was read and reading cannot help. It now names both causes and asks the caller to check the path.
Any key but title let indented and dash lines follow it, so an indented line after a closed quote or flow value, or a mapping under a plain scalar, was skipped as part of that key. The block is then YAML that does not parse, and HackMD takes the H1, while a later title: still read as the title. Nested lines now follow only an empty value, indented text only a block scalar or a plain scalar it folds into, and anything else gives no title.
docs/tools.md promised title_drift whenever the body's title differs from the listed one, but it is left out when the title cannot be read for certain, and push leaves it out of conflict and remote_changed results. A client could take a missing field as proof the titles match; the docs now say when it is reported.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bodies and titles now follow how HackMD actually stores them. HackMD reads
\r\nand a lone\rback as\n(measured 2026-10-08), so a body sent with CRLF could never be confirmed by its read-back, and a working file saved with CRLF looked changed on every sync check. Bodies a caller supplies and every local file read for sync are now converted first; a body read from HackMD and resent goes back exactly as read. An explicit title always wins and later body edits never rename a note, so pull and push now reporttitle_driftwhen the body's own title (plain front matter, or an H1 that is its first line of text) differs from the listed one. Anything the parser cannot read for certain gives none, since a wrong suggestion would rename a note. Read-back now checks an explicit title too.Pull also reports
changes, a bounded diff of what it replaced in the local file.LocalFiles::write_atomictakes anExpect(the size and modification time of the file the caller read, or absent), checked just before the rename, so an editor's save during a pull, or into a conflict's*.remote.md, is refused as a conflict instead of lost. Three smaller commits keep upstream error details intact when no token is set, skip sizing a note list the cache will not keep, and foldRemoteSourceintolist_notes.Verified with
cargo fmt --check,cargo clippy --all-targets --all-features -- -D warnings, andmake check(344 lib tests, 11 stdio tests); the third commit also passes clippy and its 325 lib tests on its own. A probe ran 2.56 million generated four-line bodies through the title parser: no panic, and no title carrying markup. The live suites were not run.Left out: how HackMD derives a title beyond the measured precedence (inline markup, repeated spaces) is unmeasured, so the parser stays narrow until it is. A conflict snapshot is still removed if its sidecar write fails right after an editor saved into it; that predates this change.
Summary by cubic
Stores note bodies in the form HackMD actually keeps and reports title drift, so a CRLF file no longer looks changed on every sync check and a renamed H1 no longer diverges from the listing silently.
Bodies and titles
\r\nand lone\rto\nin every caller-supplied body and every local file read for sync; HackMD reads those back as\n, so a resent body goes back exactly as read.hackmd_pull_noteandhackmd_push_notereporttitle_driftwhen the body's own title differs from the listed one, only when the parser can read that title for certain (plain front matter, or an H1 that is the body's first line of text). Push reports it only on a push or a no-op, so a missingtitle_driftdoes not mean the titles match.Conflict safety
changes, a bounded diff of what it replaced locally, and none when the old file was unreadable, oversized, not UTF-8, missing, or already matched.LocalFiles::write_atomictakes anExpect— the size and modification time of the file the caller read — checked just before the rename, so an editor's save during a pull or into a conflict's*.remote.mdis refused as a conflict instead of lost; a dangling symlink standing where nothing was expected is refused for the same reason.title:.RemoteSourceintolist_notes.Written for commit 1312293. Summary will update on new commits.