feat(send): link every response to the request version that produced it

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.
This commit is contained in:
Gregory Schier
2026-09-05 21:05:34 -07:00
parent 77fe1367a0
commit 19a43e3785
4 changed files with 53 additions and 3 deletions
@@ -7,6 +7,7 @@ use crate::models::{
use crate::queries::any_request::AnyRequest;
use crate::util::UpdateSource;
use crate::versions::{apply_version_document, content_hash, version_document};
use log::warn;
use sea_query::{Expr, ExprTrait, Query, SqliteQueryBuilder};
use sea_query_rusqlite::RusqliteBinder;
@@ -68,6 +69,24 @@ impl<'a> ClientDb<'a> {
self.snapshot_request(&self.get_any_request(request_id)?, reason)
}
/// What every send calls: capture the request, and don't make a fuss.
///
/// A send is not worth failing over history that couldn't be written, and
/// a request with no id is ephemeral and has nothing to version. Either way
/// the response just has no version to offer.
pub fn snapshot_request_for_send(&self, request: &AnyRequest) -> Option<String> {
if request.id().is_empty() {
return None;
}
match self.snapshot_request(request, ModelVersionReason::Send) {
Ok(version) => Some(version.id),
Err(err) => {
warn!("Failed to snapshot request before send: {err}");
None
}
}
}
/// Write a version's content back over the live request.
///
/// Anything the live request has picked up since its last version is