#619 got here first and got more of it right. Two things adopted from it, and
one backed out.
**Adopted, and this one is a bug fix.** The pair editor generates an `id` for
every header and parameter row the first time it touches a request, and that
write reaches the database like any other. Stripping only the top-level `id`
left those in, so merely opening a request minted a version whose diff was
nothing but ids. `strip_ids` drops every `id` at every depth. The regression
test fails without it.
**Adopted.** Hashes carry a `v1:` prefix, so a hash written by an algorithm a
build doesn't understand is recognizable as unreadable rather than silently
read as "different". And `sorted_keys` + `to_string` replaces the hand-rolled
canonical writer — same guarantee, less code, and byte-identical to the
implementation that is landing.
**Backed out.** `comparable()` in import.rs goes back to its own copy. #619
rewrites that function and most of the file around it, including dropping
`sortPriority` from the comparison, so pointing it at the shared module now
would collide in the hottest region of that branch to save six lines. This
module is shaped to be what #619 calls when it lands, and says so; the
convergence is one deletion away rather than a reconciliation.
That leaves `folderId` as the only real disagreement between the two callers,
which PLACEMENT_KEYS now documents as such: import counts a move between
folders as a change, versioning must not.
Import already had this: `comparable()` in import.rs, added last week, strips
identity fields off a model so a re-import can tell a change from a conflict.
Request versioning arrived with its own copy of the same idea.
Now both call `yaak_models::content`, which owns the mechanism and — more
usefully — the reasoning. Two things had no single home before: why identity
fields can never count as content, and why hashing has to sort object keys
itself rather than trusting `serde_json::Map` to be a `BTreeMap`
(`preserve_order` is on in some builds of this workspace and off in others).
The key lists stay separate on purpose, as IDENTITY_KEYS and PLACEMENT_KEYS.
Import counts a move as a change, because equality there means "same content in
the same place"; versioning must not, or dragging a request around the sidebar
would mint versions nobody asked for. A test on each side pins that difference,
since the obvious next refactor is to collapse the two lists into one and
neither behaviour would fail loudly if you did.
Directory sync deliberately keeps its own Sha1 and is not folded in. It
checksums the bytes of a file to notice someone edited it on disk, so it has to
reflect formatting and key order — the exact things this module discards.
Drives the real HTTP pipeline against a stub executor and a real database, so
the assertion is the one the feature actually rests on: a stored send leaves a
response naming the request behind it, two sends of an unchanged request name
the same version, and editing afterwards is something the response pane can
detect.
HTTP snapshots in resolve_send_inputs, the last point in the pipeline that
still holds the stored request — below it the request has been resolved against
its folder and workspace and then rendered, and neither is what a restore
should put back. Every host reaches sending through that function, so the
desktop, the CLI and plugin-triggered sends all get versions without each
knowing about them. gRPC and WebSocket connect do the same at their connection
upserts.
snapshot_request_for_send swallows its own errors: a send is not worth failing
over history that couldn't be written, and an ephemeral request has no id to
version. Either way the response just has no version to offer.