Repository navigation
Conversation
HackMD is believed to blank a note's body when a PATCH leaves content out. That is unmeasured, and the official CLI sends metadata-only PATCHes, so the destructive suite now measures it. Until it does, a metadata update reads the body and sends it back, and UpdateNoteRequest requires content, so no note PATCH can be built without one. expected_hash is checked against that same read, and is now accepted on a metadata-only update. A note whose read has no body is refused rather than blanked. client.update_note is the only note PATCH; patch edits and pushes borrow their body through it. It is never retried, even after a 429: its body was read or checked just before, and a retry after backoff would send it back over edits made while waiting. The 429 now says to read the note again first. A metadata update's read-back compares what was sent, normalized the way HackMD may store it (tags as a trimmed set, a missing description as empty, a permalink ignoring case), so a read from before the PATCH is no longer reported as its result. Title and folder are not compared: HackMD may derive the title from the body, and a folder read lists ancestors in unverified order. A folder move reports folder_placement_confirmed instead. Create's folder fallback sends the body the create sent, else the one it read, and with neither skips the PATCH. Its error tells the agent not to create the note again. A 2xx reply to a write that cannot be parsed is now kind readback, not upstream, since the write landed; image upload keeps upstream, because nothing can look for an uploaded image and a second upload only leaves an unused copy. Responses parse leniently: an unknown enum value, an odd editor profile, or a null list costs that one field rather than every note in a list or every team lookup. Note output gains suggest_edit_permission, and pull returns body_hash. The destructive suite reports, without asserting, both halves of the note PATCH contract on a personal and a team note, whether a name-only folder PATCH keeps the other fields, and whether the team folder-order route returns what was written.
check_folder_order PUT a seeded order and put the original back only after its read-back and assertions passed. A failed poll or a lost parent unwound past the restore, and the outer cleanup removes only the test's own notes and folders, so a failing run left the account's folder order changed. The original now goes back before any check can fail, including a read that panics on an HTTP error.
docs/tools.md said every note PATCH carries a body read or checked just before. A plain content replacement without expected_hash reads nothing first. It names the PATCHes that do, and says why the replacement is not retried either: after a backoff it would still land over edits made in the meantime.
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.
HackMD is believed to blank a note's body when a PATCH omits
content, so a metadata-only update (title, tags, permissions, permalink, folder) would wipe the note. This makes every note PATCH carry a body: a metadata update reads the current body and sends it back,UpdateNoteRequestrequirescontent, andexpected_hashis checked against that same read. The premise is unmeasured, and the official CLI sends metadata-only PATCHes, so the destructive suite now measures both halves of the contract (does an omittedcontentblank the body, and does a content-only PATCH keep the other fields) on a personal and a team note. If the body survives, the resend should be reverted.Along the way:
client.update_noteis the only note PATCH and is never retried, since its body was read just before and a retry after backoff would overwrite edits made while waiting; a metadata update's read-back compares what was sent, normalized, instead of accepting a stale read; create's folder fallback prefers the body it created and never sends a blank; a 2xx write reply that cannot be parsed isreadback(look, do not retry) rather thanupstream; responses parse leniently, so one odd field no longer fails a whole note list.Client-visible changes: new output fields
suggest_edit_permission, pullbody_hash, and updatefolder_placement_confirmed;expected_hashis now accepted on metadata-only updates; a metadata update on a note with no body fails with kindupstream; note PATCHes are no longer retried after a 429 or 5xx. Tool input schemas are unchanged (comparedtools/listfromHEADand this branch: same 14 tools and fields, only descriptions differ), and no error kind was renamed.Verified with
make check(280 unit and 11 stdio tests),cargo clippy --all-targets --all-features -- -D warnings,cargo fmt --check, and a release build, all run on the committed tree. Each key behavior was mutation-checked: breaking it on purpose makes a test fail. The live destructive suite was not run, since it needs the dedicated test account.Summary by cubic
Hedge against HackMD blanking a note's body when a PATCH omits
content, and keep unconfirmed writes honest.Changes
expected_hashis checked against that read, and body-less notes are refused with kindupstream.folder_placement_confirmed.readbackexcept image uploads; responses parse leniently;suggest_edit_permissionand pullbody_hashare new outputs.Verification
make check(280 unit and 11 stdio tests), clippy with-D warnings,cargo fmt --check, and a release build pass; each key behavior is mutation-checked; the live destructive suite was not run.Written for commit 3236f49. Summary will update on new commits.