    NetStreamsReply, OutputEvent, ResizeReq, SpawnReq, Spawned, SubscribeReq, KIND_APPLIED,
    KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIALED,
    KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS,
    KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA,
    KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,
    KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT, KIND_RESIZE, KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
};

    NetStreamsReply, OutputEvent, ResizeReq, SessionsReply, SpawnReq, Spawned, SubscribeReq,
    KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIALED,
    KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS,
    KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA,
    KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,
    KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,
    KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
};

decode_bytes, encode_bytes, AppliedEvent, ErrorEvent, ExitEvent, InputReq, KillReq, NetDialReq,

NetDialed, NetPresenceEvent, NetPresenceSubscribeReq, NetSent, NetStatusReply, NetStreamData,

NetStreamEof, NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq,

-    NetStreamsReply, OutputEvent, ResizeReq, SpawnReq, Spawned, SubscribeReq, KIND_APPLIED,

-    KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIALED,

+    NetStreamsReply, OutputEvent, ResizeReq, SessionsReply, SpawnReq, Spawned, SubscribeReq,

+    KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIALED,

-    KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT, KIND_RESIZE, KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,

+    KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,

+    KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,


Edit

    /// The broker's held-streams listing (D4b).
    NetStreams(NetStreamsReply),

    /// The broker's held-streams listing (D4b).
    NetStreams(NetStreamsReply),
    /// The broker's hosted-sessions listing (D9-1: session → endpoint).
    Sessions(SessionsReply),


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs has been updated successfully. (file state is current in your context — no need to Read it back)

    /// The broker's held-streams listing (D4b).
    NetStreams(NetStreamsReply),
    /// The broker's hosted-sessions listing (D9-1: session → endpoint).
    Sessions(SessionsReply),

+    /// The broker's hosted-sessions listing (D9-1: session → endpoint).

/// A contiguous chunk of a stream's reads (the cursor has advanced past it).

NetStreamData { stream_id: u64, seq: u64, bytes: Vec<u8> },

/// A stream's read side finished cleanly; `next_seq` is the completeness


    pub fn net_streams(&mut self) -> io::Result<NetStreamsReply> {
        self.send(KIND_NET_STREAMS, serde_json::Value::Null)?;
        loop {
            match self.read_event()? {
                BrokerEvent::NetStreams(ev) => return Ok(ev),
                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
                _ => continue,
            }
        }
    }

    pub fn net_streams(&mut self) -> io::Result<NetStreamsReply> {
        self.send(KIND_NET_STREAMS, serde_json::Value::Null)?;
        loop {
            match self.read_event()? {
                BrokerEvent::NetStreams(ev) => return Ok(ev),
                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
                _ => continue,
            }
        }
    }

    /// List the broker's hosted sessions (id + owning endpoint) — the inbound
    /// dispatcher's session→endpoint resolution (D9-1).
    pub fn sessions(&mut self) -> io::Result<SessionsReply> {
        self.send(KIND_SESSIONS, serde_json::Value::Null)?;
        loop {
            match self.read_event()? {
                BrokerEvent::Sessions(ev) => return Ok(ev),
                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
                _ => continue,
            }
        }
    }


    pub fn net_streams(&mut self) -> io::Result<NetStreamsReply> {
        self.send(KIND_NET_STREAMS, serde_json::Value::Null)?;
        loop {
            match self.read_event()? {
                BrokerEvent::NetStreams(ev) => return Ok(ev),
                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
                _ => continue,
            }
        }
    }

    /// List the broker's hosted sessions (id + owning endpoint) — the inbound
    /// dispatcher's session→endpoint resolution (D9-1).
    pub fn sessions(&mut self) -> io::Result<SessionsReply> {
        self.send(KIND_SESSIONS, serde_json::Value::Null)?;
        loop {
            match self.read_event()? {
                BrokerEvent::Sessions(ev) => return Ok(ev),
                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
                _ => continue,
            }
        }
    }

+    /// List the broker's hosted sessions (id + owning endpoint) — the inbound

+    /// dispatcher's session→endpoint resolution (D9-1).

+    pub fn sessions(&mut self) -> io::Result<SessionsReply> {

+        self.send(KIND_SESSIONS, serde_json::Value::Null)?;

+                BrokerEvent::Sessions(ev) => return Ok(ev),

+                BrokerEvent::Error { message } => return Err(io::Error::other(message)),

/// (Re)subscribe to a stream's buffered reads from `from_seq`, setting the

/// local cursor (gapless, exactly-once resume — the PTY `subscribe`


D9-1: production trigger loops + VerifyPolicy + spt send WAN CLI

peerloop.rs pump host (sync/update/notif/reconcile thin calls over existing drivers), DaemonConfig knobs, production VerifyPolicy assembly, spt send WAN leg, loopback E2E proving loops fire undriven. Gotchas #1/#9.

D9-2: resting-state model + transition echo + wake-edge resurface

State machine (active→dormant→suspended, wake), Status::Suspended, transition echo commune (KH 3.3), resurface_at_boundary(Wake), freshness pull trigger. REQ-INST-3/4 → [impl,unit]; KH 7.4 if multi-agent echo scheduling.

D9-3: dormancy budget measurement + warm/cold policy lock

SPT_BUDGET=1 harness on real adapters (both rig hosts), docs/DORMANCY-BUDGET.md, policy lock, ADR-0003 #9 closed. doc→REQ-INST-3.

D9-4: REQ-INST-14 resource advertisement

resources field on Instance, spt resources set/show/list projection (visibility-gated). [doc,impl,unit].

D9-5: REQ-INST-15 immutable home subnet + spt fork

Home-at-creation (auto/ask/local-until-join, no setter), sync_subnets=[home] seed, adapter-at-creation rule, spt fork copy-then-diverge (--delete-source; same-node v1). [doc,impl,unit].

D9-6: two-host E2E + runbook + gh-repo interim retirement

SPT_TWO_HOST=1 int tests (full ladder), docs/TWO-HOST-RUNBOOK.md, run on rig, int stage activations, retire gh-repo-sync interim.

D9-7: closeout sweep

required_stages audit, traceable-reqs green, ROADMAP M4-delivered, CONTEXT amendments, plan-status COMPLETE, CI matrix FINAL green.


Bash

cargo check -p spt-daemon 2>&1 | grep -E "^error" -A 8 | head -40; echo DONE

Check after sessions IPC


DONE


sed -n '36,75p' crates/spt-net/src/net/xfer.rs; grep -n "pub fn fetch_file" -A 14 crates/spt-daemon/src/xfer.rs | head -18; grep -n "pub fn push_file" -A 14 crates/spt-daemon/src/xfer.rs | head -18

XferRecord variants + operator fn signatures


/// One transfer-protocol record. Tagged (`kind`) so both directions share one
/// stream decoder; an unknown future kind fails the line's decode and is
/// skipped (degrade, don't die — same posture as every wire feed).
// [impl->REQ-REACH-1]
// [impl->REQ-HAZARD-WAN-ORIGIN-AUTH]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum XferRecord {
    /// Operator → target, first record on a pull stream: send me the file at
    /// `path` (resolved under the target's transfer root — never absolute,
    /// never traversing). `xfer_id` is the operator-minted durable transfer id.
    Fetch { path: String, xfer_id: String },
    /// Operator → target, first record on a push stream: an incoming file of
    /// `total` bytes for `path` (same root-relative rules as `Fetch`).
    Push { path: String, total: u64, xfer_id: String },
    /// One chunk of file bytes at absolute byte `offset` — positionally
    /// idempotent: a replayed chunk rewrites the same bytes in place.
    Chunk { xfer_id: String, offset: u64, data_b64: String },
    /// The sending side wrote every byte (`total` of them): commit the file
    /// (atomic temp→final rename on the receiving side). The target echoes
    /// `Done` back on a push so the operator observes the durable commit.
    Done { xfer_id: String, total: u64 },
    /// The transfer failed on the other side; nothing was committed.
    Err { xfer_id: String, message: String },
}

impl XferRecord {
    /// Encode as one NDJSON line (the JSON document + `\n`).
    pub fn encode_line(&self) -> Vec<u8> {
        ndjson::encode_line(self)
    }
}

/// The transfer stream's NDJSON reassembler — the shared
/// [`NdjsonDecoder`](crate::net::ndjson::NdjsonDecoder) typed to
/// [`XferRecord`]s. One decoder per stream side.
pub type XferDecoder = NdjsonDecoder<XferRecord>;

#[cfg(test)]
mod tests {
384:pub fn fetch_file(
385-    brain: &mut Brain,
386-    conn_id: u64,
387-    remote_path: &str,
388-    dest: &Path,
389-    xfer_id: &str,
390-    open_op: u64,
391-    progress_dir: &Path,
392-) -> io::Result<u64> {
393-    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
394-    let stream_id = opened.stream_id;
395-    brain.net_stream_subscribe(stream_id, 0)?;
396-    let line = XferRecord::Fetch { path: remote_path.to_string(), xfer_id: xfer_id.to_string() }
397-        .encode_line();
398-    brain.net_stream_send(stream_id, &line, None, false)?;
476:pub fn push_file(
477-    brain: &mut Brain,
478-    conn_id: u64,
479-    src: &Path,
480-    remote_path: &str,
481-    xfer_id: &str,
482-    open_op: u64,
483-    progress_dir: &Path,
484-) -> io::Result<u64> {
485-    let mut file = std::fs::File::open(src)?;
486-    let total = file.metadata()?.len();
487-
488-    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
489-    let stream_id = opened.stream_id;
490-    brain.net_stream_subscribe(stream_id, 0)?;


grep -rn "fetch_file\|push_file" crates/ --include=*.rs | grep -v "fn fetch_file\|fn push_file\|//" | head -8

fetch/push callers


crates/spt-daemon/src/lib.rs:194:pub use xfer::{fetch_file, push_file, serve_xfer, XferServeOutcome};
crates/spt-daemon/tests/xfer.rs:26:use spt_daemon::xfer::{fetch_file, push_file, serve_xfer, XferServeOutcome};
crates/spt-daemon/tests/xfer.rs:122:        let n = fetch_file(
crates/spt-daemon/tests/xfer.rs:160:        let err = fetch_file(
crates/spt-daemon/tests/xfer.rs:207:        push_file(&mut operator, conn.conn_id, &src, "in/drop.bin", "xferR", 2, &progress_b)


Edit

    /// Operator → target, first record on a pull stream: send me the file at
    /// `path` (resolved under the target's transfer root — never absolute,
    /// never traversing). `xfer_id` is the operator-minted durable transfer id.
    Fetch { path: String, xfer_id: String },
    /// Operator → target, first record on a push stream: an incoming file of
    /// `total` bytes for `path` (same root-relative rules as `Fetch`).
    Push { path: String, total: u64, xfer_id: String },

    /// Operator → target, first record on a pull stream: send me the file at
    /// `path` (resolved under the target's transfer root — never absolute,
    /// never traversing). `xfer_id` is the operator-minted durable transfer
    /// id. `endpoint` names whose access whitelist gates the transfer (D9-1:
    /// the dispatcher's gate subject — the *rule set* is wire-named, the
    /// *origin* it is checked against stays handshake-proven, never payload).
    Fetch {
        path: String,
        xfer_id: String,
        #[serde(default)]
        endpoint: String,
    },
    /// Operator → target, first record on a push stream: an incoming file of
    /// `total` bytes for `path` (same root-relative + gate rules as `Fetch`).
    Push {
        path: String,
        total: u64,
        xfer_id: String,
        #[serde(default)]
        endpoint: String,
    },


<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>

Error: File has not been read yet. Read it first before writing to it.


Bash

python - <<'EOF'
p = r'crates/spt-net/src/net/xfer.rs'
s = open(p, encoding='utf-8').read()
old = """    /// Operator \xe2\x86\x92 target, first record on a pull stream: send me the file at"""
print(repr(s[1200:1400]))
EOF

Inspect exact bytes around Fetch variant


'tent by\n//! construction � and [`XferRecord::Done`] commits via atomic\n//! temp-file rename, where a replayed commit of an already-renamed transfer\n//! dedups against the final file. `xfer_id` is the '


36	/// One transfer-protocol record. Tagged (`kind`) so both directions share one
37	/// stream decoder; an unknown future kind fails the line's decode and is
38	/// skipped (degrade, don't die — same posture as every wire feed).
39	// [impl->REQ-REACH-1]
40	// [impl->REQ-HAZARD-WAN-ORIGIN-AUTH]
41	#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42	#[serde(tag = "kind", rename_all = "snake_case")]
43	pub enum XferRecord {
44	    /// Operator → target, first record on a pull stream: send me the file at
45	    /// `path` (resolved under the target's transfer root — never absolute,
46	    /// never traversing). `xfer_id` is the operator-minted durable transfer id.
47	    Fetch { path: String, xfer_id: String },
48	    /// Operator → target, first record on a push stream: an incoming file of
49	    /// `total` bytes for `path` (same root-relative rules as `Fetch`).
50	    Push { path: String, total: u64, xfer_id: String },
51	    /// One chunk of file bytes at absolute byte `offset` — positionally
52	    /// idempotent: a replayed chunk rewrites the same bytes in place.
53	    Chunk { xfer_id: String, offset: u64, data_b64: String },
54	    /// The sending side wrote every byte (`total` of them): commit the file
55	    /// (atomic temp→final rename on the receiving side). The target echoes
56	    /// `Done` back on a push so the operator observes the durable commit.
57	    Done { xfer_id: String, total: u64 },
58	    /// The transfer failed on the other side; nothing was committed.
59	    Err { xfer_id: String, message: String },
60	}
61
62	impl XferRecord {
63	    /// Encode as one NDJSON line (the JSON document + `\n`).
64	    pub fn encode_line(&self) -> Vec<u8> {
65	        ndjson::encode_line(self)

/// One transfer-protocol record. Tagged (`kind`) so both directions share one
/// stream decoder; an unknown future kind fails the line's decode and is
/// skipped (degrade, don't die — same posture as every wire feed).
// [impl->REQ-REACH-1]
// [impl->REQ-HAZARD-WAN-ORIGIN-AUTH]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum XferRecord {
    /// Operator → target, first record on a pull stream: send me the file at
    /// `path` (resolved under the target's transfer root — never absolute,
    /// never traversing). `xfer_id` is the operator-minted durable transfer id.
    Fetch { path: String, xfer_id: String },
    /// Operator → target, first record on a push stream: an incoming file of
    /// `total` bytes for `path` (same root-relative rules as `Fetch`).
    Push { path: String, total: u64, xfer_id: String },
    /// One chunk of file bytes at absolute byte `offset` — positionally
    /// idempotent: a replayed chunk rewrites the same bytes in place.
    Chunk { xfer_id: String, offset: u64, data_b64: String },
    /// The sending side wrote every byte (`total` of them): commit the file
    /// (atomic temp→final rename on the receiving side). The target echoes
    /// `Done` back on a push so the operator observes the durable commit.
    Done { xfer_id: String, total: u64 },
    /// The transfer failed on the other side; nothing was committed.
    Err { xfer_id: String, message: String },
}

impl XferRecord {
    /// Encode as one NDJSON line (the JSON document + `\n`).
    pub fn encode_line(&self) -> Vec<u8> {
        ndjson::encode_line(self)


Edit

    /// Operator → target, first record on a pull stream: send me the file at
    /// `path` (resolved under the target's transfer root — never absolute,
    /// never traversing). `xfer_id` is the operator-minted durable transfer id.
    Fetch { path: String, xfer_id: String },
    /// Operator → target, first record on a push stream: an incoming file of
    /// `total` bytes for `path` (same root-relative rules as `Fetch`).
    Push { path: String, total: u64, xfer_id: String },

    /// Operator → target, first record on a pull stream: send me the file at
    /// `path` (resolved under the target's transfer root — never absolute,
    /// never traversing). `xfer_id` is the operator-minted durable transfer
    /// id. `endpoint` names whose access whitelist gates the transfer (D9-1:
    /// the dispatcher's gate subject — the *rule set* is wire-named, the
    /// *origin* it is checked against stays handshake-proven, never payload).
    Fetch {
        path: String,
        xfer_id: String,
        #[serde(default)]
        endpoint: String,
    },
    /// Operator → target, first record on a push stream: an incoming file of
    /// `total` bytes for `path` (same root-relative + gate rules as `Fetch`).
    Push {
        path: String,
        total: u64,
        xfer_id: String,
        #[serde(default)]
        endpoint: String,
    },


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-net\src\net\xfer.rs has been updated successfully. (file state is current in your context — no need to Read it back)

    /// Operator → target, first record on a pull stream: send me the file at
    /// `path` (resolved under the target's transfer root — never absolute,
    /// never traversing). `xfer_id` is the operator-minted durable transfer
    /// id. `endpoint` names whose access whitelist gates the transfer (D9-1:
    /// the dispatcher's gate subject — the *rule set* is wire-named, the
    /// *origin* it is checked against stays handshake-proven, never payload).
    Fetch {
        path: String,
        xfer_id: String,
        #[serde(default)]
        endpoint: String,
    },
    /// Operator → target, first record on a push stream: an incoming file of
    /// `total` bytes for `path` (same root-relative + gate rules as `Fetch`).
    Push {
        path: String,
        total: u64,
        xfer_id: String,
        #[serde(default)]
        endpoint: String,
    },

//! Off-node file-transfer records (M4-D5c, REQ-REACH-1) — chunked,
//! progress-queryable transfer between an instance's node and the node
//! driving it.
//!
//! Remote-control keeps compute + files on the target node (CONTEXT
//! §Remote-control vs local operation); the file channel is the deliberate,
//! addressable exception: the driving node may pull a file off the target
//! ([`XferRecord::Fetch`]) or push one onto it ([`XferRecord::Push`]). One
//! transfer rides one bidi QUIC stream (D4), framed as the shared NDJSON
//! ([`crate::net::ndjson`]) with chunk bytes base64 inside the JSON — the
//! same posture as the attach viewport, no binary fork of the codec.
//!
//! ## Idempotency is positional, not journaled (KNOWN-HAZARDS gotcha)
//!
//! Wire sends in a transfer pump are fire-and-forget — a journaled
//! `net_stream_send` blocks for its ack and discards interleaved events,
//! which starves a full-duplex pump (the D5b lesson). Exactly-once lives at
//! the edges instead: every [`XferRecord::Chunk`] carries its absolute byte
//! `offset`, so a replayed feed (a target-brain restart re-serving the stream
//! from seq 0) rewrites the same bytes at the same positions — idempotent by
//! construction — and [`XferRecord::Done`] commits via atomic
//! temp-file rename, where a replayed commit of an already-renamed transfer
//! dedups against the final file. `xfer_id` is the sender-minted durable id
//! that names the transfer end-to-end: the progress query key on both ends
//! (CONTEXT §file-transfer progress) and the temp-file discriminator.
//!
//! Like [`WanMessage`](crate::net::wanmsg::WanMessage), transfer records
//! carry **no origin field** — the access gate's subject is the QUIC
//! handshake identity from the broker's stream table (KNOWN-HAZARDS 7.5,
//! REQ-HAZARD-WAN-ORIGIN-AUTH).

use serde::{Deserialize, Serialize};

use crate::net::ndjson::{self, NdjsonDecoder};

/// One transfer-protocol record. Tagged (`kind`) so both directions share one
/// stream decoder; an unknown future kind fails the line's decode and is
/// skipped (degrade, don't die — same posture as every wire feed).
// [impl->REQ-REACH-1]
// [impl->REQ-HAZARD-WAN-ORIGIN-AUTH]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum XferRecord {
    /// Operator → target, first record on a pull stream: send me the file at
    /// `path` (resolved under the target's transfer root — never absolute,
    /// never traversing). `xfer_id` is the operator-minted durable transfer id.
    Fetch { path: String, xfer_id: String },
    /// Operator → target, first record on a push stream: an incoming file of
    /// `total` bytes for `path` (same root-relative rules as `Fetch`).
    Push { path: String, total: u64, xfer_id: String },
    /// One chunk of file bytes at absolute byte `offset` — positionally
    /// idempotent: a replayed chunk rewrites the same bytes in place.
    Chunk { xfer_id: String, offset: u64, data_b64: String },
    /// The sending side wrote every byte (`total` of them): commit the file
    /// (atomic temp→final rename on the receiving side). The target echoes
    /// `Done` back on a push so the operator observes the durable commit.
    Done { xfer_id: String, total: u64 },
    /// The transfer failed on the other side; nothing was committed.
    Err { xfer_id: String, message: String },
}

impl XferRecord {
    /// Encode as one NDJSON line (the JSON document + `\n`).
    pub fn encode_line(&self) -> Vec<u8> {
        ndjson::encode_line(self)
    }
}

/// The transfer stream's NDJSON reassembler — the shared
/// [`NdjsonDecoder`](crate::net::ndjson::NdjsonDecoder) typed to
/// [`XferRecord`]s. One decoder per stream side.
pub type XferDecoder = NdjsonDecoder<XferRecord>;

#[cfg(test)]
mod tests {
    use super::*;

    // [unit->REQ-REACH-1] every record kind round-trips through the NDJSON
    // line encoding, and the decoder skips an unknown future kind without
    // wedging the stream (forward compat: degrade, don't die).
    #[test]
    fn records_round_trip_and_unknown_kind_is_skipped() {
        let records = vec![
            XferRecord::Fetch { path: "notes/plan.md".into(), xfer_id: "n:1".into() },
            XferRecord::Push { path: "in/drop.bin".into(), total: 9, xfer_id: "n:2".into() },
            XferRecord::Chunk { xfer_id: "n:2".into(), offset: 4096, data_b64: "aGk=".into() },
            XferRecord::Done { xfer_id: "n:2".into(), total: 9 },
            XferRecord::Err { xfer_id: "n:1".into(), message: "no such file".into() },
        ];
        let mut wire = Vec::new();
        for r in &records {
            wire.extend_from_slice(&r.encode_line());
        }
        let mut with_junk = records[0].encode_line();
        with_junk.extend_from_slice(b"{\"kind\":\"added_at_d9\",\"x\":1}\n");
        with_junk.extend_from_slice(&records[3].encode_line());

        let mut dec = XferDecoder::new();
        assert_eq!(dec.push(&wire), records, "all kinds round-trip in order");

        let mut dec = XferDecoder::new();
        assert_eq!(
            dec.push(&with_junk),
            vec![records[0].clone(), records[3].clone()],
            "unknown kind skipped, feed not wedged"
        );
    }

    // [unit->REQ-REACH-1] chunk-split safety rides the shared decoder: a
    // record split mid-bytes across pushes reassembles (QUIC chunking is not
    // record framing).
    #[test]
    fn record_split_across_chunks_reassembles() {
        let r = XferRecord::Chunk { xfer_id: "n:7".into(), offset: 0, data_b64: "c3BsaXQ=".into() };
        let line = r.encode_line();
        let mut dec = XferDecoder::new();
        assert!(dec.push(&line[..line.len() / 2]).is_empty());
        assert_eq!(dec.push(&line[line.len() / 2..]), vec![r]);
    }

    // [unit->REQ-HAZARD-WAN-ORIGIN-AUTH] transfer records carry no origin
    // field: a forged `origin_node` (or `node`) inside the JSON decodes as an
    // ignored unknown field — nothing in the decoded record can impersonate a
    // node. The gate's subject comes from the QUIC handshake (the broker's
    // stream table), asserted end-to-end in the daemon's xfer int tests.
    #[test]
    fn forged_origin_field_is_inert() {
        let wire =
            br#"{"kind":"fetch","path":"a.txt","xfer_id":"n:1","origin_node":"evil","node":"evil"}"#;
        let decoded: XferRecord = serde_json::from_slice(wire).unwrap();
        // Every field the record actually has is accounted for — there is no
        // place for the forged origin to land.
        assert_eq!(decoded, XferRecord::Fetch { path: "a.txt".into(), xfer_id: "n:1".into() });
    }
}

-    /// never traversing). `xfer_id` is the operator-minted durable transfer id.

-    Fetch { path: String, xfer_id: String },

+    /// never traversing). `xfer_id` is the operator-minted durable transfer

+    /// id. `endpoint` names whose access whitelist gates the transfer (D9-1:

+    /// the dispatcher's gate subject — the *rule set* is wire-named, the

+    /// *origin* it is checked against stays handshake-proven, never payload).

-    /// `total` bytes for `path` (same root-relative rules as `Fetch`).

-    Push { path: String, total: u64, xfer_id: String },

+    /// `total` bytes for `path` (same root-relative + gate rules as `Fetch`).


Bash

cargo check -p spt-net -p spt-daemon --all-targets 2>&1 | grep -E "^error" -A 10 | head -60

Find all XferRecord pattern breakages


error[E0063]: missing field `endpoint` in initializer of `net::xfer::XferRecord`
  --> crates\spt-net\src\net\xfer.rs:98:13
   |
98 |             XferRecord::Fetch { path: "notes/plan.md".into(), xfer_id: "n:1".into() },
   |             ^^^^^^^^^^^^^^^^^ missing `endpoint`

error[E0063]: missing field `endpoint` in initializer of `net::xfer::XferRecord`
  --> crates\spt-net\src\net\xfer.rs:99:13
   |
99 |             XferRecord::Push { path: "in/drop.bin".into(), total: 9, xfer_id: "n:2".into() },
   |             ^^^^^^^^^^^^^^^^ missing `endpoint`

error[E0063]: missing field `endpoint` in initializer of `net::xfer::XferRecord`
   --> crates\spt-net\src\net\xfer.rs:147:29
    |
147 |         assert_eq!(decoded, XferRecord::Fetch { path: "a.txt".into(), xfer_id: "n:1".into() });
    |                             ^^^^^^^^^^^^^^^^^ missing `endpoint`

For more information about this error, try `rustc --explain E0063`.
error: could not compile `spt-net` (lib test) due to 3 previous errors
warning: build failed, waiting for other jobs to finish...
error[E0027]: pattern does not mention field `endpoint`
   --> crates\spt-daemon\src\xfer.rs:238:25
    |
238 |                         XferRecord::Fetch { path, xfer_id } => {
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `endpoint`
    |
help: include the missing field in the pattern
    |
238 |                         XferRecord::Fetch { path, xfer_id, endpoint } => {
    |                                                          ++++++++++
help: if you don't care about this missing field, you can explicitly ignore it
--
error[E0027]: pattern does not mention field `endpoint`
   --> crates\spt-daemon\src\xfer.rs:298:25
    |
298 |                         XferRecord::Push { path, total, xfer_id } => {
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `endpoint`
    |
help: include the missing field in the pattern
    |
298 |                         XferRecord::Push { path, total, xfer_id, endpoint } => {
    |                                                                ++++++++++
help: if you don't care about this missing field, you can explicitly ignore it
--
error[E0063]: missing field `endpoint` in initializer of `XferRecord`
   --> crates\spt-daemon\src\xfer.rs:396:16
    |
396 |     let line = XferRecord::Fetch { path: remote_path.to_string(), xfer_id: xfer_id.to_string() }
    |                ^^^^^^^^^^^^^^^^^ missing `endpoint`

error[E0063]: missing field `endpoint` in initializer of `XferRecord`
   --> crates\spt-daemon\src\xfer.rs:491:16
    |
491 |     let push = XferRecord::Push {
    |                ^^^^^^^^^^^^^^^^ missing `endpoint`

Some errors have detailed explanations: E0027, E0063.
For more information about an error, try `rustc --explain E0027`.
error: could not compile `spt-daemon` (lib) due to 4 previous errors


python - <<'EOF'
import io
# spt-net xfer.rs test fixes
p = 'crates/spt-net/src/net/xfer.rs'
s = open(p, encoding='utf-8').read()
s = s.replace('XferRecord::Fetch { path: "notes/plan.md".into(), xfer_id: "n:1".into() }',
              'XferRecord::Fetch { path: "notes/plan.md".into(), xfer_id: "n:1".into(), endpoint: String::new() }')
s = s.replace('XferRecord::Push { path: "in/drop.bin".into(), total: 9, xfer_id: "n:2".into() }',
              'XferRecord::Push { path: "in/drop.bin".into(), total: 9, xfer_id: "n:2".into(), endpoint: String::new() }')
s = s.replace('XferRecord::Fetch { path: "a.txt".into(), xfer_id: "n:1".into() }',
              'XferRecord::Fetch { path: "a.txt".into(), xfer_id: "n:1".into(), endpoint: String::new() }')
open(p, 'w', encoding='utf-8', newline='').write(s)
print('net ok')
EOF

Fix spt-net xfer test initializers


net ok


1	//! Off-node file-transfer records (M4-D5c, REQ-REACH-1) — chunked,
2	//! progress-queryable transfer between an instance's node and the node
3	//! driving it.
4	//!
5	//! Remote-control keeps compute + files on the target node (CONTEXT
6	//! §Remote-control vs local operation); the file channel is the deliberate,
7	//! addressable exception: the driving node may pull a file off the target
8	//! ([`XferRecord::Fetch`]) or push one onto it ([`XferRecord::Push`]). One
9	//! transfer rides one bidi QUIC stream (D4), framed as the shared NDJSON
10	//! ([`crate::net::ndjson`]) with chunk bytes base64 inside the JSON — the
11	//! same posture as the attach viewport, no binary fork of the codec.
12	//!
13	//! ## Idempotency is positional, not journaled (KNOWN-HAZARDS gotcha)
14	//!
15	//! Wire sends in a transfer pump are fire-and-forget — a journaled
16	//! `net_stream_send` blocks for its ack and discards interleaved events,
17	//! which starves a full-duplex pump (the D5b lesson). Exactly-once lives at
18	//! the edges instead: every [`XferRecord::Chunk`] carries its absolute byte
19	//! `offset`, so a replayed feed (a target-brain restart re-serving the stream
20	//! from seq 0) rewrites the same bytes at the same positions — idempotent by
21	//! construction — and [`XferRecord::Done`] commits via atomic
22	//! temp-file rename, where a replayed commit of an already-renamed transfer
23	//! dedups against the final file. `xfer_id` is the sender-minted durable id
24	//! that names the transfer end-to-end: the progress query key on both ends
25	//! (CONTEXT §file-transfer progress) and the temp-file discriminator.
26	//!
27	//! Like [`WanMessage`](crate::net::wanmsg::WanMessage), transfer records
28	//! carry **no origin field** — the access gate's subject is the QUIC
29	//! handshake identity from the broker's stream table (KNOWN-HAZARDS 7.5,
30	//! REQ-HAZARD-WAN-ORIGIN-AUTH).
31
32	use serde::{Deserialize, Serialize};
33
34	use crate::net::ndjson::{self, NdjsonDecoder};
35
36	/// One transfer-protocol record. Tagged (`kind`) so both directions share one
37	/// stream decoder; an unknown future kind fails the line's decode and is
38	/// skipped (degrade, don't die — same posture as every wire feed).
39	// [impl->REQ-REACH-1]
40	// [impl->REQ-HAZARD-WAN-ORIGIN-AUTH]
41	#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42	#[serde(tag = "kind", rename_all = "snake_case")]
43	pub enum XferRecord {
44	    /// Operator → target, first record on a pull stream: send me the file at
45	    /// `path` (resolved under the target's transfer root — never absolute,
46	    /// never traversing). `xfer_id` is the operator-minted durable transfer
47	    /// id. `endpoint` names whose access whitelist gates the transfer (D9-1:
48	    /// the dispatcher's gate subject — the *rule set* is wire-named, the
49	    /// *origin* it is checked against stays handshake-proven, never payload).
50	    Fetch {
51	        path: String,
52	        xfer_id: String,
53	        #[serde(default)]
54	        endpoint: String,
55	    },
56	    /// Operator → target, first record on a push stream: an incoming file of
57	    /// `total` bytes for `path` (same root-relative + gate rules as `Fetch`).
58	    Push {
59	        path: String,
60	        total: u64,
61	        xfer_id: String,
62	        #[serde(default)]
63	        endpoint: String,
64	    },
65	    /// One chunk of file bytes at absolute byte `offset` — positionally
66	    /// idempotent: a replayed chunk rewrites the same bytes in place.
67	    Chunk { xfer_id: String, offset: u64, data_b64: String },
68	    /// The sending side wrote every byte (`total` of them): commit the file
69	    /// (atomic temp→final rename on the receiving side). The target echoes
70	    /// `Done` back on a push so the operator observes the durable commit.
71	    Done { xfer_id: String, total: u64 },
72	    /// The transfer failed on the other side; nothing was committed.
73	    Err { xfer_id: String, message: String },
74	}
75
76	impl XferRecord {
77	    /// Encode as one NDJSON line (the JSON document + `\n`).
78	    pub fn encode_line(&self) -> Vec<u8> {
79	        ndjson::encode_line(self)
80	    }
81	}
82
83	/// The transfer stream's NDJSON reassembler — the shared
84	/// [`NdjsonDecoder`](crate::net::ndjson::NdjsonDecoder) typed to
85	/// [`XferRecord`]s. One decoder per stream side.
86	pub type XferDecoder = NdjsonDecoder<XferRecord>;
87
88	#[cfg(test)]
89	mod tests {
90	    use super::*;
91
92	    // [unit->REQ-REACH-1] every record kind round-trips through the NDJSON
93	    // line encoding, and the decoder skips an unknown future kind without
94	    // wedging the stream (forward compat: degrade, don't die).
95	    #[test]
96	    fn records_round_trip_and_unknown_kind_is_skipped() {
97	        let records = vec![
98	            XferRecord::Fetch { path: "notes/plan.md".into(), xfer_id: "n:1".into(), endpoint: String::new() },
99	            XferRecord::Push { path: "in/drop.bin".into(), total: 9, xfer_id: "n:2".into(), endpoint: String::new() },
100	            XferRecord::Chunk { xfer_id: "n:2".into(), offset: 4096, data_b64: "aGk=".into() },
101	            XferRecord::Done { xfer_id: "n:2".into(), total: 9 },
102	            XferRecord::Err { xfer_id: "n:1".into(), message: "no such file".into() },
103	        ];
104	        let mut wire = Vec::new();
105	        for r in &records {
106	            wire.extend_from_slice(&r.encode_line());
107	        }
108	        let mut with_junk = records[0].encode_line();
109	        with_junk.extend_from_slice(b"{\"kind\":\"added_at_d9\",\"x\":1}\n");
110	        with_junk.extend_from_slice(&records[3].encode_line());
111
112	        let mut dec = XferDecoder::new();
113	        assert_eq!(dec.push(&wire), records, "all kinds round-trip in order");
114
115	        let mut dec = XferDecoder::new();
116	        assert_eq!(
117	            dec.push(&with_junk),
118	            vec![records[0].clone(), records[3].clone()],
119	            "unknown kind skipped, feed not wedged"
120	        );
121	    }
122
123	    // [unit->REQ-REACH-1] chunk-split safety rides the shared decoder: a
124	    // record split mid-bytes across pushes reassembles (QUIC chunking is not
125	    // record framing).
126	    #[test]
127	    fn record_split_across_chunks_reassembles() {
128	        let r = XferRecord::Chunk { xfer_id: "n:7".into(), offset: 0, data_b64: "c3BsaXQ=".into() };
129	        let line = r.encode_line();
130	        let mut dec = XferDecoder::new();
131	        assert!(dec.push(&line[..line.len() / 2]).is_empty());
132	        assert_eq!(dec.push(&line[line.len() / 2..]), vec![r]);
133	    }
134
135	    // [unit->REQ-HAZARD-WAN-ORIGIN-AUTH] transfer records carry no origin
136	    // field: a forged `origin_node` (or `node`) inside the JSON decodes as an
137	    // ignored unknown field — nothing in the decoded record can impersonate a
138	    // node. The gate's subject comes from the QUIC handshake (the broker's
139	    // stream table), asserted end-to-end in the daemon's xfer int tests.
140	    #[test]
141	    fn forged_origin_field_is_inert() {
142	        let wire =
143	            br#"{"kind":"fetch","path":"a.txt","xfer_id":"n:1","origin_node":"evil","node":"evil"}"#;
144	        let decoded: XferRecord = serde_json::from_slice(wire).unwrap();
145	        // Every field the record actually has is accounted for — there is no
146	        // place for the forged origin to land.
147	        assert_eq!(decoded, XferRecord::Fetch { path: "a.txt".into(), xfer_id: "n:1".into(), endpoint: String::new() });
148	    }
149	}


225	    let refuse = |brain: &mut Brain, xfer_id: &str, message: String| -> io::Result<XferServeOutcome> {
226	        let line = XferRecord::Err { xfer_id: xfer_id.to_string(), message: message.clone() }
227	            .encode_line();
228	        brain.net_stream_send(stream_id, &line, None, true)?;
229	        Ok(XferServeOutcome::Failed(message))
230	    };
231
232	    loop {
233	        match brain.read_event()? {
234	            BrokerEvent::NetStreamData { stream_id: sid, bytes, .. } if sid == stream_id => {
235	                for rec in decoder.push(&bytes) {
236	                    match rec {
237	                        // ── Pull: read the file, pump it onto the wire. ────
238	                        XferRecord::Fetch { path, xfer_id } => {
239	                            // Unsolicited wire inbound — the ADR-0009 gate,
240	                            // under the transport-proven origin.
241	                            if access_check(endpoint, origin_node, InboundClass::Unsolicited)
242	                                == AccessDecision::Deny
243	                            {
244	                                brain.net_stream_send(stream_id, &[], None, true)?;
245	                                return Ok(XferServeOutcome::Refused);
246	                            }
247	                            let src = match resolve_under(root, &path) {
248	                                Ok(p) => p,
249	                                Err(m) => return refuse(brain, &xfer_id, m),
250	                            };
251	                            let mut file = match std::fs::File::open(&src) {
252	                                Ok(f) => f,
253	                                Err(e) => {
254	                                    return refuse(brain, &xfer_id, format!("open {path:?}: {e}"))
255	                                }
256	                            };
257	                            let total = file.metadata()?.len();
258	                            let _ = write_progress_at(
259	                                progress_dir,
260	                                XferRole::Send,
261	                                &progress(&xfer_id, &path, 0, total, "active"),
262	                            );
263	                            let mut offset = 0u64;
264	                            let mut buf = vec![0u8; CHUNK_BYTES];
265	                            loop {
266	                                let n = file.read(&mut buf)?;
267	                                if n == 0 {
268	                                    break;
269	                                }
270	                                let line = XferRecord::Chunk {
271	                                    xfer_id: xfer_id.clone(),
272	                                    offset,
273	                                    data_b64: encode_bytes(&buf[..n]),
274	                                }
275	                                .encode_line();
276	                                // Fire-and-forget by design (module docs):
277	                                // the offset is the dedup, not a send journal.
278	                                brain.net_stream_send(stream_id, &line, None, false)?;
279	                                offset += n as u64;
280	                                let _ = write_progress_at(
281	                                    progress_dir,
282	                                    XferRole::Send,
283	                                    &progress(&xfer_id, &path, offset, total, "active"),
284	                                );
285	                            }
286	                            let done =
287	                                XferRecord::Done { xfer_id: xfer_id.clone(), total }.encode_line();
288	                            brain.net_stream_send(stream_id, &done, None, true)?;
289	                            let _ = write_progress_at(
290	                                progress_dir,
291	                                XferRole::Send,
292	                                &progress(&xfer_id, &path, total, total, "done"),
293	                            );
294	                            return Ok(XferServeOutcome::Sent { bytes: total });
295	                        }
296
297	                        // ── Push: receive chunks, commit at Done. ──────────
298	                        XferRecord::Push { path, total, xfer_id } => {
299	                            if access_check(endpoint, origin_node, InboundClass::Unsolicited)
300	                                == AccessDecision::Deny
301	                            {
302	                                brain.net_stream_send(stream_id, &[], None, true)?;
303	                                return Ok(XferServeOutcome::Refused);
304	                            }
305	                            let dest = match resolve_under(root, &path) {
306	                                Ok(p) => p,
307	                                Err(m) => return refuse(brain, &xfer_id, m),
308	                            };
309	                            let state = match RecvState::open(&xfer_id, &path, dest, total) {
310	                                Ok(s) => s,
311	                                Err(e) => {
312	                                    return refuse(brain, &xfer_id, format!("open {path:?}: {e}"))
313	                                }
314	                            };
315	                            let _ = write_progress_at(
316	                                progress_dir,
317	                                XferRole::Recv,
318	                                &progress(&xfer_id, &path, 0, total, "active"),
319	                            );

    let refuse = |brain: &mut Brain, xfer_id: &str, message: String| -> io::Result<XferServeOutcome> {
        let line = XferRecord::Err { xfer_id: xfer_id.to_string(), message: message.clone() }
            .encode_line();
        brain.net_stream_send(stream_id, &line, None, true)?;
        Ok(XferServeOutcome::Failed(message))
    };

    loop {
        match brain.read_event()? {
            BrokerEvent::NetStreamData { stream_id: sid, bytes, .. } if sid == stream_id => {
                for rec in decoder.push(&bytes) {
                    match rec {
                        // ── Pull: read the file, pump it onto the wire. ────
                        XferRecord::Fetch { path, xfer_id } => {
                            // Unsolicited wire inbound — the ADR-0009 gate,
                            // under the transport-proven origin.
                            if access_check(endpoint, origin_node, InboundClass::Unsolicited)
                                == AccessDecision::Deny
                            {
                                brain.net_stream_send(stream_id, &[], None, true)?;
                                return Ok(XferServeOutcome::Refused);
                            }
                            let src = match resolve_under(root, &path) {
                                Ok(p) => p,
                                Err(m) => return refuse(brain, &xfer_id, m),
                            };
                            let mut file = match std::fs::File::open(&src) {
                                Ok(f) => f,
                                Err(e) => {
                                    return refuse(brain, &xfer_id, format!("open {path:?}: {e}"))
                                }
                            };
                            let total = file.metadata()?.len();
                            let _ = write_progress_at(
                                progress_dir,
                                XferRole::Send,
                                &progress(&xfer_id, &path, 0, total, "active"),
                            );
                            let mut offset = 0u64;
                            let mut buf = vec![0u8; CHUNK_BYTES];
                            loop {
                                let n = file.read(&mut buf)?;
                                if n == 0 {
                                    break;
                                }
                                let line = XferRecord::Chunk {
                                    xfer_id: xfer_id.clone(),
                                    offset,
                                    data_b64: encode_bytes(&buf[..n]),
                                }
                                .encode_line();
                                // Fire-and-forget by design (module docs):
                                // the offset is the dedup, not a send journal.
                                brain.net_stream_send(stream_id, &line, None, false)?;
                                offset += n as u64;
                                let _ = write_progress_at(
                                    progress_dir,
                                    XferRole::Send,
                                    &progress(&xfer_id, &path, offset, total, "active"),
                                );
                            }
                            let done =
                                XferRecord::Done { xfer_id: xfer_id.clone(), total }.encode_line();
                            brain.net_stream_send(stream_id, &done, None, true)?;
                            let _ = write_progress_at(
                                progress_dir,
                                XferRole::Send,
                                &progress(&xfer_id, &path, total, total, "done"),
                            );
                            return Ok(XferServeOutcome::Sent { bytes: total });
                        }

                        // ── Push: receive chunks, commit at Done. ──────────
                        XferRecord::Push { path, total, xfer_id } => {
                            if access_check(endpoint, origin_node, InboundClass::Unsolicited)
                                == AccessDecision::Deny
                            {
                                brain.net_stream_send(stream_id, &[], None, true)?;
                                return Ok(XferServeOutcome::Refused);
                            }
                            let dest = match resolve_under(root, &path) {
                                Ok(p) => p,
                                Err(m) => return refuse(brain, &xfer_id, m),
                            };
                            let state = match RecvState::open(&xfer_id, &path, dest, total) {
                                Ok(s) => s,
                                Err(e) => {
                                    return refuse(brain, &xfer_id, format!("open {path:?}: {e}"))
                                }
                            };
                            let _ = write_progress_at(
                                progress_dir,
                                XferRole::Recv,
                                &progress(&xfer_id, &path, 0, total, "active"),
                            );


D9-7: closeout sweep


Edit

XferRecord::Fetch { path, xfer_id } => {

                        // The record's own `endpoint` field is the DISPATCHER's
                        // resolution input — here the caller already supplied
                        // the gate subject, so the wire copy is ignored.
                        XferRecord::Fetch { path, xfer_id, endpoint: _ } => {


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\xfer.rs has been updated successfully. (file state is current in your context — no need to Read it back)

                        // The record's own `endpoint` field is the DISPATCHER's
                        // resolution input — here the caller already supplied
                        // the gate subject, so the wire copy is ignored.
                        XferRecord::Fetch { path, xfer_id, endpoint: _ } => {

// ── Pull: read the file, pump it onto the wire. ────

-                        XferRecord::Fetch { path, xfer_id } => {

+                        // The record's own `endpoint` field is the DISPATCHER's

+                        // resolution input — here the caller already supplied

+                        // the gate subject, so the wire copy is ignored.

+                        XferRecord::Fetch { path, xfer_id, endpoint: _ } => {

// Unsolicited wire inbound — the ADR-0009 gate,

if access_check(endpoint, origin_node, InboundClass::Unsolicited)


XferRecord::Push { path, total, xfer_id } => {

XferRecord::Push { path, total, xfer_id, endpoint: _ } => {


// ── Push: receive chunks, commit at Done. ──────────

-                        XferRecord::Push { path, total, xfer_id } => {

+                        XferRecord::Push { path, total, xfer_id, endpoint: _ } => {


375	            _ => {}
376	        }
377	    }
378	}
379
380	/// Operator-side: pull `remote_path` (root-relative on the target) off the
381	/// target into `dest`, blocking until the file is committed locally. Returns
382	/// the byte count. `open_op` is the durable id for the exactly-once stream
383	/// open (safe to block for — no transfer traffic exists before the stream
384	/// does); everything after rides fire-and-forget + positional dedup.
385	/// Progress for this (receiving) end lands in `progress_dir` per chunk.
386	// [impl->REQ-REACH-1]
387	pub fn fetch_file(
388	    brain: &mut Brain,
389	    conn_id: u64,
390	    remote_path: &str,
391	    dest: &Path,
392	    xfer_id: &str,
393	    open_op: u64,
394	    progress_dir: &Path,
395	) -> io::Result<u64> {
396	    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
397	    let stream_id = opened.stream_id;
398	    brain.net_stream_subscribe(stream_id, 0)?;
399	    let line = XferRecord::Fetch { path: remote_path.to_string(), xfer_id: xfer_id.to_string() }
400	        .encode_line();
401	    brain.net_stream_send(stream_id, &line, None, false)?;
402
403	    if let Some(parent) = dest.parent() {
404	        std::fs::create_dir_all(parent)?;
405	    }
406	    let mut state = RecvState {
407	        xfer_id: xfer_id.to_string(),
408	        wire_path: remote_path.to_string(),
409	        final_path: dest.to_path_buf(),
410	        temp: temp_path(dest, xfer_id),
411	        file: Some(
412	            std::fs::OpenOptions::new()
413	                .create(true)
414	                .truncate(false)
415	                .write(true)
416	                .open(temp_path(dest, xfer_id))?,
417	        ),
418	        total: 0, // unknown until Done (chunks carry offsets, not totals)
419	        done: 0,

            _ => {}
        }
    }
}

/// Operator-side: pull `remote_path` (root-relative on the target) off the
/// target into `dest`, blocking until the file is committed locally. Returns
/// the byte count. `open_op` is the durable id for the exactly-once stream
/// open (safe to block for — no transfer traffic exists before the stream
/// does); everything after rides fire-and-forget + positional dedup.
/// Progress for this (receiving) end lands in `progress_dir` per chunk.
// [impl->REQ-REACH-1]
pub fn fetch_file(
    brain: &mut Brain,
    conn_id: u64,
    remote_path: &str,
    dest: &Path,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let line = XferRecord::Fetch { path: remote_path.to_string(), xfer_id: xfer_id.to_string() }
        .encode_line();
    brain.net_stream_send(stream_id, &line, None, false)?;

    if let Some(parent) = dest.parent() {
        std::fs::create_dir_all(parent)?;
    }
    let mut state = RecvState {
        xfer_id: xfer_id.to_string(),
        wire_path: remote_path.to_string(),
        final_path: dest.to_path_buf(),
        temp: temp_path(dest, xfer_id),
        file: Some(
            std::fs::OpenOptions::new()
                .create(true)
                .truncate(false)
                .write(true)
                .open(temp_path(dest, xfer_id))?,
        ),
        total: 0, // unknown until Done (chunks carry offsets, not totals)
        done: 0,


pub fn fetch_file(
    brain: &mut Brain,
    conn_id: u64,
    remote_path: &str,
    dest: &Path,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let line = XferRecord::Fetch { path: remote_path.to_string(), xfer_id: xfer_id.to_string() }
        .encode_line();
    brain.net_stream_send(stream_id, &line, None, false)?;

#[allow(clippy::too_many_arguments)]
pub fn fetch_file(
    brain: &mut Brain,
    conn_id: u64,
    endpoint: &str,
    remote_path: &str,
    dest: &Path,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let line = XferRecord::Fetch {
        path: remote_path.to_string(),
        xfer_id: xfer_id.to_string(),
        endpoint: endpoint.to_string(),
    }
    .encode_line();
    brain.net_stream_send(stream_id, &line, None, false)?;


pub fn fetch_file(
    brain: &mut Brain,
    conn_id: u64,
    remote_path: &str,
    dest: &Path,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let line = XferRecord::Fetch { path: remote_path.to_string(), xfer_id: xfer_id.to_string() }
        .encode_line();
    brain.net_stream_send(stream_id, &line, None, false)?;

#[allow(clippy::too_many_arguments)]
pub fn fetch_file(
    brain: &mut Brain,
    conn_id: u64,
    endpoint: &str,
    remote_path: &str,
    dest: &Path,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let line = XferRecord::Fetch {
        path: remote_path.to_string(),
        xfer_id: xfer_id.to_string(),
        endpoint: endpoint.to_string(),
    }
    .encode_line();
    brain.net_stream_send(stream_id, &line, None, false)?;

/// does); everything after rides fire-and-forget + positional dedup.

/// Progress for this (receiving) end lands in `progress_dir` per chunk.

-    let line = XferRecord::Fetch { path: remote_path.to_string(), xfer_id: xfer_id.to_string() }

+        endpoint: endpoint.to_string(),


485	pub fn push_file(
486	    brain: &mut Brain,
487	    conn_id: u64,
488	    src: &Path,
489	    remote_path: &str,
490	    xfer_id: &str,
491	    open_op: u64,
492	    progress_dir: &Path,
493	) -> io::Result<u64> {
494	    let mut file = std::fs::File::open(src)?;
495	    let total = file.metadata()?.len();
496
497	    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
498	    let stream_id = opened.stream_id;
499	    brain.net_stream_subscribe(stream_id, 0)?;
500	    let push = XferRecord::Push {
501	        path: remote_path.to_string(),
502	        total,
503	        xfer_id: xfer_id.to_string(),
504	    }
505	    .encode_line();
506	    brain.net_stream_send(stream_id, &push, None, false)?;
507	    let _ = write_progress_at(
508	        progress_dir,
509	        XferRole::Send,
510	        &progress(xfer_id, remote_path, 0, total, "active"),
511	    );
512
513	    let mut offset = 0u64;
514	    let mut buf = vec![0u8; CHUNK_BYTES];
515	    loop {
516	        let n = file.read(&mut buf)?;
517	        if n == 0 {
518	            break;
519	        }
520	        let line = XferRecord::Chunk {
521	            xfer_id: xfer_id.to_string(),
522	            offset,
523	            data_b64: encode_bytes(&buf[..n]),
524	        }

pub fn push_file(
    brain: &mut Brain,
    conn_id: u64,
    src: &Path,
    remote_path: &str,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let mut file = std::fs::File::open(src)?;
    let total = file.metadata()?.len();

    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let push = XferRecord::Push {
        path: remote_path.to_string(),
        total,
        xfer_id: xfer_id.to_string(),
    }
    .encode_line();
    brain.net_stream_send(stream_id, &push, None, false)?;
    let _ = write_progress_at(
        progress_dir,
        XferRole::Send,
        &progress(xfer_id, remote_path, 0, total, "active"),
    );

    let mut offset = 0u64;
    let mut buf = vec![0u8; CHUNK_BYTES];
    loop {
        let n = file.read(&mut buf)?;
        if n == 0 {
            break;
        }
        let line = XferRecord::Chunk {
            xfer_id: xfer_id.to_string(),
            offset,
            data_b64: encode_bytes(&buf[..n]),
        }


pub fn push_file(
    brain: &mut Brain,
    conn_id: u64,
    src: &Path,
    remote_path: &str,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let mut file = std::fs::File::open(src)?;
    let total = file.metadata()?.len();

    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let push = XferRecord::Push {
        path: remote_path.to_string(),
        total,
        xfer_id: xfer_id.to_string(),
    }
    .encode_line();

#[allow(clippy::too_many_arguments)]
pub fn push_file(
    brain: &mut Brain,
    conn_id: u64,
    endpoint: &str,
    src: &Path,
    remote_path: &str,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let mut file = std::fs::File::open(src)?;
    let total = file.metadata()?.len();

    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let push = XferRecord::Push {
        path: remote_path.to_string(),
        total,
        xfer_id: xfer_id.to_string(),
        endpoint: endpoint.to_string(),
    }
    .encode_line();


pub fn push_file(
    brain: &mut Brain,
    conn_id: u64,
    src: &Path,
    remote_path: &str,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let mut file = std::fs::File::open(src)?;
    let total = file.metadata()?.len();

    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let push = XferRecord::Push {
        path: remote_path.to_string(),
        total,
        xfer_id: xfer_id.to_string(),
    }
    .encode_line();

#[allow(clippy::too_many_arguments)]
pub fn push_file(
    brain: &mut Brain,
    conn_id: u64,
    endpoint: &str,
    src: &Path,
    remote_path: &str,
    xfer_id: &str,
    open_op: u64,
    progress_dir: &Path,
) -> io::Result<u64> {
    let mut file = std::fs::File::open(src)?;
    let total = file.metadata()?.len();

    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
    let stream_id = opened.stream_id;
    brain.net_stream_subscribe(stream_id, 0)?;
    let push = XferRecord::Push {
        path: remote_path.to_string(),
        total,
        xfer_id: xfer_id.to_string(),
        endpoint: endpoint.to_string(),
    }
    .encode_line();

/// stream open is the only journaled op; chunks dedup by offset on the far

/// side. Progress for this (sending) end lands in `progress_dir` per chunk.

brain.net_stream_send(stream_id, &push, None, false)?;


Now the dispatch arms:


        StreamFamily::Notif => serve_notif_feed(&mut brain, stream_id, origin),
        StreamFamily::WanMsg => serve_wan_feed(&mut brain, stream_id, origin, paths),
        StreamFamily::Registry => serve_registry_feed(&mut brain, stream_id, origin, registry),
        StreamFamily::Attach | StreamFamily::Xfer => DispatchOutcome::Unwired(family),
        StreamFamily::Unknown => DispatchOutcome::Unknown,
    }
}

        StreamFamily::Notif => serve_notif_feed(&mut brain, stream_id, origin),
        StreamFamily::WanMsg => serve_wan_feed(&mut brain, stream_id, origin, paths),
        StreamFamily::Registry => serve_registry_feed(&mut brain, stream_id, origin, registry),
        StreamFamily::Attach => {
            // The gate subject is the session's OWNING endpoint, resolved
            // from the broker's table (the spawn's label) — never from wire
            // bytes. An unlabeled or unknown session refuses, fail-closed.
            let session_id = line.get("session_id").and_then(|v| v.as_u64());
            let endpoint = session_id.and_then(|sid| {
                brain.sessions().ok().and_then(|reply| {
                    reply
                        .sessions
                        .iter()
                        .find(|s| s.session_id == sid && !s.endpoint.is_empty())
                        .map(|s| s.endpoint.clone())
                })
            });
            match endpoint {
                Some(endpoint) => {
                    match serve_attach(&mut brain, stream_id, &endpoint, origin, 0) {
                        Ok(out) => DispatchOutcome::Served(format!("{out:?}")),
                        Err(e) => DispatchOutcome::Failed(e.to_string()),
                    }
                }
                None => {
                    // Refuse politely: finish our send side, claim stays.
                    let _ = brain.net_stream_send(stream_id, &[], None, true);
                    DispatchOutcome::Served("attach refused: unlabeled/unknown session".into())
                }
            }
        }
        StreamFamily::Xfer => {
            // The wire names whose whitelist gates (the rule set); the origin
            // it is checked against stays handshake-proven. Blank refuses.
            let endpoint = line
                .get("endpoint")
                .and_then(|v| v.as_str())
                .filter(|e| !e.is_empty())
                .map(str::to_string);
            match endpoint {
                Some(endpoint) => {
                    let root = spt_store::xfer::transfers_dir();
                    let progress = paths.scratch.join("xfer-progress");
                    match serve_xfer(&mut brain, stream_id, &endpoint, origin, 0, &root, &progress)
                    {
                        Ok(out) => DispatchOutcome::Served(format!("{out:?}")),
                        Err(e) => DispatchOutcome::Failed(e.to_string()),
                    }
                }
                None => {
                    let _ = brain.net_stream_send(stream_id, &[], None, true);
                    DispatchOutcome::Served("xfer refused: no endpoint named".into())
                }
            }
        }
        StreamFamily::Unknown => DispatchOutcome::Unknown,
    }
}


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\dispatch.rs has been updated successfully. (file state is current in your context — no need to Read it back)

        StreamFamily::Notif => serve_notif_feed(&mut brain, stream_id, origin),
        StreamFamily::WanMsg => serve_wan_feed(&mut brain, stream_id, origin, paths),
        StreamFamily::Registry => serve_registry_feed(&mut brain, stream_id, origin, registry),
        StreamFamily::Attach | StreamFamily::Xfer => DispatchOutcome::Unwired(family),
        StreamFamily::Unknown => DispatchOutcome::Unknown,
    }
}

        StreamFamily::Notif => serve_notif_feed(&mut brain, stream_id, origin),
        StreamFamily::WanMsg => serve_wan_feed(&mut brain, stream_id, origin, paths),
        StreamFamily::Registry => serve_registry_feed(&mut brain, stream_id, origin, registry),
        StreamFamily::Attach => {
            // The gate subject is the session's OWNING endpoint, resolved
            // from the broker's table (the spawn's label) — never from wire
            // bytes. An unlabeled or unknown session refuses, fail-closed.
            let session_id = line.get("session_id").and_then(|v| v.as_u64());
            let endpoint = session_id.and_then(|sid| {
                brain.sessions().ok().and_then(|reply| {
                    reply
                        .sessions
                        .iter()
                        .find(|s| s.session_id == sid && !s.endpoint.is_empty())
                        .map(|s| s.endpoint.clone())
                })
            });
            match endpoint {
                Some(endpoint) => {
                    match serve_attach(&mut brain, stream_id, &endpoint, origin, 0) {
                        Ok(out) => DispatchOutcome::Served(format!("{out:?}")),
                        Err(e) => DispatchOutcome::Failed(e.to_string()),
                    }
                }
                None => {
                    // Refuse politely: finish our send side, claim stays.
                    let _ = brain.net_stream_send(stream_id, &[], None, true);
                    DispatchOutcome::Served("attach refused: unlabeled/unknown session".into())
                }
            }
        }
        StreamFamily::Xfer => {
            // The wire names whose whitelist gates (the rule set); the origin
            // it is checked against stays handshake-proven. Blank refuses.
            let endpoint = line
                .get("endpoint")
                .and_then(|v| v.as_str())
                .filter(|e| !e.is_empty())
                .map(str::to_string);
            match endpoint {
                Some(endpoint) => {
                    let root = spt_store::xfer::transfers_dir();
                    let progress = paths.scratch.join("xfer-progress");
                    match serve_xfer(&mut brain, stream_id, &endpoint, origin, 0, &root, &progress)
                    {
                        Ok(out) => DispatchOutcome::Served(format!("{out:?}")),
                        Err(e) => DispatchOutcome::Failed(e.to_string()),
                    }
                }
                None => {
                    let _ = brain.net_stream_send(stream_id, &[], None, true);
                    DispatchOutcome::Served("xfer refused: no endpoint named".into())
                }
            }
        }
        StreamFamily::Unknown => DispatchOutcome::Unknown,
    }
}

-        StreamFamily::Attach | StreamFamily::Xfer => DispatchOutcome::Unwired(family),

+            // The gate subject is the session's OWNING endpoint, resolved

+            // from the broker's table (the spawn's label) — never from wire

+            // bytes. An unlabeled or unknown session refuses, fail-closed.

+            let session_id = line.get("session_id").and_then(|v| v.as_u64());

+            let endpoint = session_id.and_then(|sid| {

+                brain.sessions().ok().and_then(|reply| {

+                        .find(|s| s.session_id == sid && !s.endpoint.is_empty())

+                        .map(|s| s.endpoint.clone())

+                    match serve_attach(&mut brain, stream_id, &endpoint, origin, 0) {

+                        Ok(out) => DispatchOutcome::Served(format!("{out:?}")),

+                        Err(e) => DispatchOutcome::Failed(e.to_string()),

+                    // Refuse politely: finish our send side, claim stays.

+                    let _ = brain.net_stream_send(stream_id, &[], None, true);

+                    DispatchOutcome::Served("attach refused: unlabeled/unknown session".into())

+            // The wire names whose whitelist gates (the rule set); the origin

+            // it is checked against stays handshake-proven. Blank refuses.

+                .and_then(|v| v.as_str())

+                .filter(|e| !e.is_empty())

+                    let root = spt_store::xfer::transfers_dir();

+                    let progress = paths.scratch.join("xfer-progress");

+                    match serve_xfer(&mut brain, stream_id, &endpoint, origin, 0, &root, &progress)

+                    DispatchOutcome::Served("xfer refused: no endpoint named".into())


use crate::brain::{Brain, BrokerEvent};
use crate::notifsync::{apply_notif_feed, NotifPolicy};
use crate::propagate::serve_update;
use crate::registryhost::{RegistryGatePolicy, RegistryHost};
use crate::relcache::ReleaseCache;
use crate::sync::{serve_sync, SyncPolicy};
use crate::wan::receive_wan;

use crate::attach::serve_attach;
use crate::brain::{Brain, BrokerEvent};
use crate::notifsync::{apply_notif_feed, NotifPolicy};
use crate::propagate::serve_update;
use crate::registryhost::{RegistryGatePolicy, RegistryHost};
use crate::relcache::ReleaseCache;
use crate::sync::{serve_sync, SyncPolicy};
use crate::wan::receive_wan;
use crate::xfer::serve_xfer;


use crate::attach::serve_attach;
use crate::brain::{Brain, BrokerEvent};
use crate::notifsync::{apply_notif_feed, NotifPolicy};
use crate::propagate::serve_update;
use crate::registryhost::{RegistryGatePolicy, RegistryHost};
use crate::relcache::ReleaseCache;
use crate::sync::{serve_sync, SyncPolicy};
use crate::wan::receive_wan;
use crate::xfer::serve_xfer;

/// Poll cadence for the stream-table watch (the dispatcher's only busy-wait;

/// workers block on their stream's event feed).


<task-notification>
<task-id>b40vc2uij</task-id>
<tool-use-id>toolu_017ARqeFMtVuiwxuEG5MbY4e</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\648159b6-16b2-4568-b643-7b0c98c1871d\tasks\b40vc2uij.output</output-file>
<status>completed</status>
<summary>Background command "Poll CI until FINAL for af7f394" completed (exit code 0)</summary>
</task-notification>


/// Why a dispatched stream's worker ended — loud + typed, never silent.
#[derive(Debug)]
pub enum DispatchOutcome {
    /// The family's serve loop ran to its own outcome (rendered for the log).
    Served(String),
    /// Classified, but the family's production wiring lands in a later slice.
    Unwired(StreamFamily),
    /// The first line never arrived / didn't parse — dropped.
    Unknown,
    /// The worker failed (IO against broker or store).
    Failed(String),
}

/// Why a dispatched stream's worker ended — loud + typed, never silent.
#[derive(Debug)]
pub enum DispatchOutcome {
    /// The family's serve loop ran to its own outcome (rendered for the log).
    Served(String),
    /// The first line never arrived / didn't parse — dropped.
    Unknown,
    /// The worker failed (IO against broker or store).
    Failed(String),
}


/// Why a dispatched stream's worker ended — loud + typed, never silent.
#[derive(Debug)]
pub enum DispatchOutcome {
    /// The family's serve loop ran to its own outcome (rendered for the log).
    Served(String),
    /// Classified, but the family's production wiring lands in a later slice.
    Unwired(StreamFamily),
    /// The first line never arrived / didn't parse — dropped.
    Unknown,
    /// The worker failed (IO against broker or store).
    Failed(String),
}

/// Why a dispatched stream's worker ended — loud + typed, never silent.
#[derive(Debug)]
pub enum DispatchOutcome {
    /// The family's serve loop ran to its own outcome (rendered for the log).
    Served(String),
    /// The first line never arrived / didn't parse — dropped.
    Unknown,
    /// The worker failed (IO against broker or store).
    Failed(String),
}

-    /// Classified, but the family's production wiring lands in a later slice.


Prompt:
<task-notification>
<task-id>b40vc2uij</task-id>
<tool-use-id>toolu_017ARqeFMtVuiwxuEG5MbY4e</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\648159b6-16b2-4568-b643-7b0c98c1871d\tasks\b40vc2uij.output</output-file>
<status>completed</status>
<summary>Background command "Poll CI until FINAL for af7f394" completed (exit code 0)</summary>
</task-notification>


Bash

tail -3 "C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\648159b6-16b2-4568-b643-7b0c98c1871d\tasks\b40vc2uij.output"; cargo check -p spt-daemon --all-targets 2>&1 | grep -E "^error" -A 8 | head -40; echo CHECK_DONE

CI result for af7f394 + compile check


FINAL:
success
error[E0063]: missing field `endpoint` in initializer of `SpawnReq`
  --> crates\spt-daemon\tests\daemon_e2e.rs:60:5
   |
60 |     SpawnReq { program, args, rows: 24, cols: 80 }
   |     ^^^^^^^^ missing `endpoint`

error[E0063]: missing field `endpoint` in initializer of `SpawnReq`
  --> crates\spt-daemon\tests\brain_swap.rs:46:5
   |
46 |     SpawnReq { program, args, rows: 24, cols: 80 }
   |     ^^^^^^^^ missing `endpoint`

error[E0063]: missing field `endpoint` in initializer of `SpawnReq`
  --> crates\spt-daemon\tests\digest.rs:44:5
   |
44 |     SpawnReq { program, args, rows: 24, cols: 80 }
   |     ^^^^^^^^ missing `endpoint`

error[E0063]: missing field `endpoint` in initializer of `SpawnReq`
  --> crates\spt-daemon\tests\broker.rs:46:5
   |
46 |     SpawnReq { program, args, rows: 24, cols: 80 }
   |     ^^^^^^^^ missing `endpoint`

error[E0063]: missing field `endpoint` in initializer of `SpawnReq`
  --> crates\spt-daemon\tests\attach.rs:91:5
   |
91 |     SpawnReq { program, args, rows: 24, cols: 80 }
   |     ^^^^^^^^ missing `endpoint`

error[E0061]: this function takes 8 arguments but 7 arguments were supplied
   --> crates\spt-daemon\tests\xfer.rs:122:17
    |
122 |         let n = fetch_file(
    |                 ^^^^^^^^^^
...
126 |             &dest_clone,
    |             ----------- expected `&str`, found `&PathBuf`
127 |             "xfer1",
--
CHECK_DONE


D9-1-4 CI-green. Fixing test initializers:


python - <<'EOF'
import glob, re
fixed = []
for p in glob.glob('crates/spt-daemon/tests/*.rs') + glob.glob('crates/spt-daemon/src/*.rs') + glob.glob('crates/spt/src/**/*.rs', recursive=True):
    s = open(p, encoding='utf-8').read()
    o = s
    s = s.replace('SpawnReq { program, args, rows: 24, cols: 80 }',
                  'SpawnReq { program, args, rows: 24, cols: 80, endpoint: String::new() }')
    if s != o:
        open(p, 'w', encoding='utf-8', newline='').write(s)
        fixed.append(p)
print(fixed)
EOF
grep -rn "SpawnReq {" crates/ --include=*.rs | grep -v "endpoint" | grep -v "pub struct" | head

Fix SpawnReq initializers across tests


['crates/spt-daemon/tests\\attach.rs', 'crates/spt-daemon/tests\\brain_swap.rs', 'crates/spt-daemon/tests\\broker.rs', 'crates/spt-daemon/tests\\daemon_e2e.rs', 'crates/spt-daemon/tests\\digest.rs', 'crates/spt-daemon/tests\\handoff.rs', 'crates/spt-daemon/tests\\idempotent.rs']
crates/spt-daemon/tests/attach.rs:86:fn echo_spawn_req() -> SpawnReq {
crates/spt-daemon/tests/brain_swap.rs:41:fn echo_spawn_req() -> SpawnReq {
crates/spt-daemon/tests/broker.rs:41:fn echo_spawn_req() -> SpawnReq {
crates/spt-daemon/tests/daemon_e2e.rs:55:fn echo_req() -> SpawnReq {
crates/spt-daemon/tests/digest.rs:39:fn echo_req() -> SpawnReq {
crates/spt-daemon/tests/handoff.rs:36:fn echo_spawn_req() -> SpawnReq {
crates/spt-daemon/tests/idempotent.rs:44:fn echo_spawn_req() -> SpawnReq {


sed -n '118,132p' crates/spt-daemon/tests/xfer.rs; sed -n '155,170p' crates/spt-daemon/tests/xfer.rs; sed -n '203,212p' crates/spt-daemon/tests/xfer.rs

xfer test call sites


    let dest = dir.path().join("fetched").join("plan.md");
    let dest_clone = dest.clone();
    let progress_b_clone = progress_b.clone();
    let fetcher = thread::spawn(move || {
        let n = fetch_file(
            &mut operator,
            conn.conn_id,
            "notes/plan.md",
            &dest_clone,
            "xfer1",
            2,
            &progress_b_clone,
        )
        .expect("fetch");
        (n, operator)
    // Confinement: a traversal fetch is refused with an Err record (the
    // operator's fetch errors; nothing outside the root is served).
    let conn2 = operator.net_dial(target.net_status().expect("a").addr, Some(3)).expect("dial2");
    let dest2 = dir.path().join("stolen.txt");
    let fetcher2 = thread::spawn(move || {
        let err = fetch_file(
            &mut operator, conn2.conn_id, "../effects.log", &dest2, "xfer2", 4, &progress_b,
        )
        .expect_err("traversal must be refused");
        err.to_string()
    });
    let (stream2, origin2) = wait_for_stream_except(&mut target, &[stream_a]);
    let outcome2 =
        serve_xfer(&mut target, stream2, "ling", &origin2, 0, &root, &progress_a).expect("serve2");
    assert!(matches!(outcome2, XferServeOutcome::Failed(_)), "refused, not served: {outcome2:?}");
    let msg = fetcher2.join().expect("fetch2 thread");
    let mut operator = connect_retry(&name_b);
    let conn = operator.net_dial(a_addr, Some(1)).expect("dial");
    let total = bytes.len() as u64;
    let pusher = thread::spawn(move || {
        push_file(&mut operator, conn.conn_id, &src, "in/drop.bin", "xferR", 2, &progress_b)
            .expect("push")
    });

    // Life 1 receives manually (the serve loop's exact write path, stepped so
    // the crash point is deterministic): open the temp the protocol names,


python - <<'EOF'
p = 'crates/spt-daemon/tests/xfer.rs'
s = open(p, encoding='utf-8').read()
s = s.replace('''        let n = fetch_file(
            &mut operator,
            conn.conn_id,
            "notes/plan.md",''', '''        let n = fetch_file(
            &mut operator,
            conn.conn_id,
            "ling",
            "notes/plan.md",''')
s = s.replace('''        let err = fetch_file(
            &mut operator, conn2.conn_id, "../effects.log", &dest2, "xfer2", 4, &progress_b,
        )''', '''        let err = fetch_file(
            &mut operator, conn2.conn_id, "ling", "../effects.log", &dest2, "xfer2", 4,
            &progress_b,
        )''')
s = s.replace('''        push_file(&mut operator, conn.conn_id, &src, "in/drop.bin", "xferR", 2, &progress_b)
            .expect("push")''', '''        push_file(
            &mut operator, conn.conn_id, "ling", &src, "in/drop.bin", "xferR", 2, &progress_b,
        )
        .expect("push")''')
open(p, 'w', encoding='utf-8', newline='').write(s)
print('ok')
EOF
cargo check -p spt-daemon -p spt --all-targets 2>&1 | grep -E "^error" -A 8 | head -40; echo CHECK_DONE

Fix xfer test callers + re-check


ok
CHECK_DONE


grep -n "whitelist\|access" crates/spt-daemon/tests/attach.rs | head -8; grep -n "pub fn access_check" -A 20 crates/spt-daemon/src/access.rs | head -28

Access gate defaults


86:pub fn access_check(endpoint: &str, origin_node: &str, class: InboundClass) -> AccessDecision {
87-    // Same-node is local operation that happened to arrive over a loopback
88-    // dial — never whitelisted away.
89-    if local_node_hex().as_deref() == Some(origin_node) {
90-        return AccessDecision::Allow;
91-    }
92-    let store = AccessStore::load();
93-    let Some(entry) = store.lookup(endpoint) else {
94-        // Absent = default-open (ADR-0009).
95-        return AccessDecision::Allow;
96-    };
97-    match class {
98-        // Established/related: the endpoint spoke to that node first.
99-        InboundClass::Reply => AccessDecision::Allow,
100-        InboundClass::Unsolicited => {
101-            if entry.nodes.iter().any(|n| n == origin_node) {
102-                AccessDecision::Allow
103-            } else {
104-                AccessDecision::Deny
105-            }
106-        }


sed -n '80,100p' crates/spt-daemon/tests/attach.rs; grep -n "pub fn request_attach" -A 16 crates/spt-daemon/src/attach.rs; grep -n "pub fn send_attach_input" -A 10 crates/spt-daemon/src/attach.rs

Echo spawn + operator attach fns


        }
        thread::sleep(Duration::from_millis(10));
    }
    panic!("brain could not connect");
}

fn echo_spawn_req() -> SpawnReq {
    #[cfg(unix)]
    let (program, args) = ("cat".to_string(), Vec::<String>::new());
    #[cfg(windows)]
    let (program, args) = ("findstr".to_string(), vec![".".to_string()]);
    SpawnReq { program, args, rows: 24, cols: 80, endpoint: String::new() }
}

/// Poll the brain's `net-streams` until a stream appears; return
/// `(stream_id, remote_id_hex)`.
fn wait_for_stream(brain: &mut Brain) -> (u64, String) {
    for _ in 0..400 {
        let reply = brain.net_streams().expect("net-streams");
        if let Some(s) = reply.streams.first() {
            return (s.stream_id, s.remote_id_hex.clone());
185:pub fn request_attach(
186-    brain: &mut Brain,
187-    conn_id: u64,
188-    session_id: u64,
189-    from_seq: u64,
190-    open_op: u64,
191-) -> io::Result<u64> {
192-    let opened = brain.net_open_stream(conn_id, Some(open_op))?;
193-    let line = ndjson::encode_line(&AttachRecord::Request { session_id, from_seq });
194-    brain.net_stream_send(opened.stream_id, &line, None, false)?;
195-    Ok(opened.stream_id)
196-}
197-
198-/// Operator-side: send one input record (the keystrokes) onto an attach
199-/// stream. `wire_op` is the operator-minted durable id the TARGET journals the
200-/// PTY write under — exactly-once at the keys, however often the record
201-/// crosses the wire. The send itself is unjournaled (see module docs).
203:pub fn send_attach_input(
204-    brain: &mut Brain,
205-    stream_id: u64,
206-    bytes: &[u8],
207-    wire_op: u64,
208-) -> io::Result<()> {
209-    let line = ndjson::encode_line(&AttachRecord::Input {
210-        data_b64: encode_bytes(bytes),
211-        op_id: wire_op,
212-    });
213-    brain.net_stream_send(stream_id, &line, None, false)?;


Adding attach + xfer undriven E2Es to tests/dispatch.rs:


Edit

// [unit->REQ-NET-1] an unclassifiable stream is dropped without wedging the
// dispatcher: garbage first, then a valid notif feed still converges.

// [int->REQ-INST-8] [int->REQ-REACH-1] remote-drive is served end to end by
// the dispatcher: B hosts a LABELED session; A attaches by session id, types,
// and reads the echo — the gate subject came from the broker's table (the
// spawn label), not wire bytes. An UNLABELED session refuses, fail-closed.
#[test]
fn dispatcher_serves_a_remote_drive_attach_undriven() {
    init_home();
    let _g = LOCK.lock().unwrap_or_else(|p| p.into_inner());
    let dir = TempDir::new().unwrap();
    let (a_name, b_name) = (unique_name(), unique_name());
    let _a = net_broker(&a_name, dir.path());
    let _b = net_broker(&b_name, dir.path());
    let stop = spawn_dispatcher_for(&b_name, &dir.path().join("scratch-b"));

    // B hosts one labeled echo session and one unlabeled one.
    #[cfg(unix)]
    let (program, args) = ("cat".to_string(), Vec::<String>::new());
    #[cfg(windows)]
    let (program, args) = ("findstr".to_string(), vec![".".to_string()]);
    let mut b_host = connect_retry(&b_name);
    let labeled = b_host
        .spawn_session(spt_daemon::msg::SpawnReq {
            program: program.clone(),
            args: args.clone(),
            rows: 24,
            cols: 80,
            endpoint: "ling-drive".to_string(),
        })
        .expect("spawn labeled");
    let unlabeled = b_host
        .spawn_session(spt_daemon::msg::SpawnReq {
            program,
            args,
            rows: 24,
            cols: 80,
            endpoint: String::new(),
        })
        .expect("spawn unlabeled");

    let mut a = connect_retry(&a_name);
    let mut b_probe = connect_retry(&b_name);
    let addr = b_probe.net_status().expect("status").addr;
    let conn = a.net_dial(addr, Some(op())).expect("dial");

    // Unlabeled session: the dispatcher refuses (our read side finishes
    // without a single Output record).
    let refused =
        spt_daemon::attach::request_attach(&mut a, conn.conn_id, unlabeled, 0, op())
            .expect("open refused viewport");
    a.net_stream_subscribe(refused, 0).expect("subscribe");
    loop {
        match a.read_event().expect("event") {
            spt_daemon::brain::BrokerEvent::NetStreamEof { stream_id, .. }
                if stream_id == refused =>
            {
                break; // refused: clean finish, no output ever
            }
            spt_daemon::brain::BrokerEvent::NetStreamData { stream_id, bytes, .. }
                if stream_id == refused && !bytes.is_empty() =>
            {
                panic!("an unlabeled session must never serve output");
            }
            _ => {}
        }
    }

    // Labeled session: attach, type, read the echo back — all undriven.
    let viewport =
        spt_daemon::attach::request_attach(&mut a, conn.conn_id, labeled, 0, op())
            .expect("open viewport");
    a.net_stream_subscribe(viewport, 0).expect("subscribe");
    spt_daemon::attach::send_attach_input(&mut a, viewport, b"dispatch-driven\r\n", op())
        .expect("type");
    let mut echoed = Vec::new();
    let deadline = std::time::Instant::now() + Duration::from_secs(10);
    let mut decoder = spt_net::net::attach::AttachDecoder::new();
    while std::time::Instant::now() < deadline {
        match a.read_event().expect("event") {
            spt_daemon::brain::BrokerEvent::NetStreamData { stream_id, bytes, .. }
                if stream_id == viewport =>
            {
                for rec in decoder.push(&bytes) {
                    if let spt_net::net::attach::AttachRecord::Output { data_b64, .. } = rec {
                        echoed.extend(
                            spt_daemon::msg::decode_bytes(&data_b64).expect("b64"),
                        );
                    }
                }
                if String::from_utf8_lossy(&echoed).contains("dispatch-driven") {
                    break;
                }
            }
            _ => {}
        }
    }
    assert!(
        String::from_utf8_lossy(&echoed).contains("dispatch-driven"),
        "the dispatcher served the viewport undriven; got {:?}",
        String::from_utf8_lossy(&echoed)
    );
    stop.store(true, Ordering::Relaxed);
}

// [int->REQ-REACH-1] a file fetch is served end to end by the dispatcher:
// the wire names the gating endpoint; the file pumps back and commits.
#[test]
fn dispatcher_serves_a_file_fetch_undriven() {
    init_home();
    let _g = LOCK.lock().unwrap_or_else(|p| p.into_inner());
    let dir = TempDir::new().unwrap();
    let (a_name, b_name) = (unique_name(), unique_name());
    let _a = net_broker(&a_name, dir.path());
    let _b = net_broker(&b_name, dir.path());
    let stop = spawn_dispatcher_for(&b_name, &dir.path().join("scratch-b"));

    // The served file lives under B's canonical transfer root.
    let root = spt_store::xfer::transfers_dir();
    std::fs::create_dir_all(root.join("notes")).unwrap();
    std::fs::write(root.join("notes").join("d915.md"), b"fetched through dispatch").unwrap();

    let mut a = connect_retry(&a_name);
    let mut b_probe = connect_retry(&b_name);
    let addr = b_probe.net_status().expect("status").addr;
    let conn = a.net_dial(addr, Some(op())).expect("dial");

    let dest = dir.path().join("pulled.md");
    let n = spt_daemon::xfer::fetch_file(
        &mut a,
        conn.conn_id,
        "ling-files",
        "notes/d915.md",
        &dest,
        "d915-x1",
        op(),
        &dir.path().join("progress-a"),
    )
    .expect("fetch");
    assert_eq!(n, 24);
    assert_eq!(std::fs::read(&dest).unwrap(), b"fetched through dispatch");
    stop.store(true, Ordering::Relaxed);
}

// [unit->REQ-NET-1] an unclassifiable stream is dropped without wedging the
// dispatcher: garbage first, then a valid notif feed still converges.


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\tests\dispatch.rs has been updated successfully. (file state is current in your context — no need to Read it back)

// [int->REQ-INST-8] [int->REQ-REACH-1] remote-drive is served end to end by
// the dispatcher: B hosts a LABELED session; A attaches by session id, types,
// and reads the echo — the gate subject came from the broker's table (the
// spawn label), not wire bytes. An UNLABELED session refuses, fail-closed.
#[test]
fn dispatcher_serves_a_remote_drive_attach_undriven() {
    init_home();
    let _g = LOCK.lock().unwrap_or_else(|p| p.into_inner());
    let dir = TempDir::new().unwrap();
    let (a_name, b_name) = (unique_name(), unique_name());
    let _a = net_broker(&a_name, dir.path());
    let _b = net_broker(&b_name, dir.path());
    let stop = spawn_dispatcher_for(&b_name, &dir.path().join("scratch-b"));

    // B hosts one labeled echo session and one unlabeled one.
    #[cfg(unix)]
    let (program, args) = ("cat".to_string(), Vec::<String>::new());
    #[cfg(windows)]
    let (program, args) = ("findstr".to_string(), vec![".".to_string()]);
    let mut b_host = connect_retry(&b_name);
    let labeled = b_host
        .spawn_session(spt_daemon::msg::SpawnReq {
            program: program.clone(),
            args: args.clone(),
            rows: 24,
            cols: 80,
            endpoint: "ling-drive".to_string(),
        })
        .expect("spawn labeled");
    let unlabeled = b_host
        .spawn_session(spt_daemon::msg::SpawnReq {
            program,
            args,
            rows: 24,
            cols: 80,
            endpoint: String::new(),
        })
        .expect("spawn unlabeled");

    let mut a = connect_retry(&a_name);
    let mut b_probe = connect_retry(&b_name);
    let addr = b_probe.net_status().expect("status").addr;
    let conn = a.net_dial(addr, Some(op())).expect("dial");

    // Unlabeled session: the dispatcher refuses (our read side finishes
    // without a single Output record).
    let refused =
        spt_daemon::attach::request_attach(&mut a, conn.conn_id, unlabeled, 0, op())
            .expect("open refused viewport");
    a.net_stream_subscribe(refused, 0).expect("subscribe");
    loop {
        match a.read_event().expect("event") {
            spt_daemon::brain::BrokerEvent::NetStreamEof { stream_id, .. }
                if stream_id == refused =>
            {
                break; // refused: clean finish, no output ever
            }
            spt_daemon::brain::BrokerEvent::NetStreamData { stream_id, bytes, .. }
                if stream_id == refused && !bytes.is_empty() =>
            {
                panic!("an unlabeled session must never serve output");
            }
            _ => {}
        }
    }

    // Labeled session: attach, type, read the echo back — all undriven.
    let viewport =
        spt_daemon::attach::request_attach(&mut a, conn.conn_id, labeled, 0, op())
            .expect("open viewport");
    a.net_stream_subscribe(viewport, 0).expect("subscribe");
    spt_daemon::attach::send_attach_input(&mut a, viewport, b"dispatch-driven\r\n", op())
        .expect("type");
    let mut echoed = Vec::new();
    let deadline = std::time::Instant::now() + Duration::from_secs(10);
    let mut decoder = spt_net::net::attach::AttachDecoder::new();
    while std::time::Instant::now() < deadline {
        match a.read_event().expect("event") {
            spt_daemon::brain::BrokerEvent::NetStreamData { stream_id, bytes, .. }
                if stream_id == viewport =>
            {
                for rec in decoder.push(&bytes) {
                    if let spt_net::net::attach::AttachRecord::Output { data_b64, .. } = rec {
                        echoed.extend(
                            spt_daemon::msg::decode_bytes(&data_b64).expect("b64"),
                        );
                    }
                }
                if String::from_utf8_lossy(&echoed).contains("dispatch-driven") {
                    break;
                }
            }
            _ => {}
        }
    }
    assert!(
        String::from_utf8_lossy(&echoed).contains("dispatch-driven"),
        "the dispatcher served the viewport undriven; got {:?}",
        String::from_utf8_lossy(&echoed)
    );
    stop.store(true, Ordering::Relaxed);
}

// [int->REQ-REACH-1] a file fetch is served end to end by the dispatcher:
// the wire names the gating endpoint; the file pumps back and commits.
#[test]
fn dispatcher_serves_a_file_fetch_undriven() {
    init_home();
    let _g = LOCK.lock().unwrap_or_else(|p| p.into_inner());
    let dir = TempDir::new().unwrap();
    let (a_name, b_name) = (unique_name(), unique_name());
    let _a = net_broker(&a_name, dir.path());
    let _b = net_broker(&b_name, dir.path());
    let stop = spawn_dispatcher_for(&b_name, &dir.path().join("scratch-b"));

    // The served file lives under B's canonical transfer root.
    let root = spt_store::xfer::transfers_dir();
    std::fs::create_dir_all(root.join("notes")).unwrap();
    std::fs::write(root.join("notes").join("d915.md"), b"fetched through dispatch").unwrap();

    let mut a = connect_retry(&a_name);
    let mut b_probe = connect_retry(&b_name);
    let addr = b_probe.net_status().expect("status").addr;
    let conn = a.net_dial(addr, Some(op())).expect("dial");

    let dest = dir.path().join("pulled.md");
    let n = spt_daemon::xfer::fetch_file(
        &mut a,
        conn.conn_id,
        "ling-files",
        "notes/d915.md",
        &dest,
        "d915-x1",
        op(),
        &dir.path().join("progress-a"),
    )
    .expect("fetch");
    assert_eq!(n, 24);
    assert_eq!(std::fs::read(&dest).unwrap(), b"fetched through dispatch");
    stop.store(true, Ordering::Relaxed);
}

// [unit->REQ-NET-1] an unclassifiable stream is dropped without wedging the
// dispatcher: garbage first, then a valid notif feed still converges.

+// [int->REQ-INST-8] [int->REQ-REACH-1] remote-drive is served end to end by

+// the dispatcher: B hosts a LABELED session; A attaches by session id, types,

+// and reads the echo — the gate subject came from the broker's table (the

+// spawn label), not wire bytes. An UNLABELED session refuses, fail-closed.

+    let _g = LOCK.lock().unwrap_or_else(|p| p.into_inner());

+    let (a_name, b_name) = (unique_name(), unique_name());

+    let _a = net_broker(&a_name, dir.path());

+    let _b = net_broker(&b_name, dir.path());

+    let stop = spawn_dispatcher_for(&b_name, &dir.path().join("scratch-b"));

+    // B hosts one labeled echo session and one unlabeled one.

+    let (program, args) = ("cat".to_string(), Vec::<String>::new());

+    let (program, args) = ("findstr".to_string(), vec![".".to_string()]);

+    let mut b_host = connect_retry(&b_name);

+        .spawn_session(spt_daemon::msg::SpawnReq {

+            endpoint: "ling-drive".to_string(),

+    let mut a = connect_retry(&a_name);

+    let mut b_probe = connect_retry(&b_name);

+    let addr = b_probe.net_status().expect("status").addr;

+    let conn = a.net_dial(addr, Some(op())).expect("dial");

+    // Unlabeled session: the dispatcher refuses (our read side finishes

+    // without a single Output record).

+        spt_daemon::attach::request_attach(&mut a, conn.conn_id, unlabeled, 0, op())

+            .expect("open refused viewport");

+    a.net_stream_subscribe(refused, 0).expect("subscribe");

+        match a.read_event().expect("event") {

+            spt_daemon::brain::BrokerEvent::NetStreamEof { stream_id, .. }

+                if stream_id == refused =>

+                break; // refused: clean finish, no output ever

+            spt_daemon::brain::BrokerEvent::NetStreamData { stream_id, bytes, .. }

+                if stream_id == refused && !bytes.is_empty() =>

+                panic!("an unlabeled session must never serve output");

+    // Labeled session: attach, type, read the echo back — all undriven.

+        spt_daemon::attach::request_attach(&mut a, conn.conn_id, labeled, 0, op())

+    a.net_stream_subscribe(viewport, 0).expect("subscribe");

+    spt_daemon::attach::send_attach_input(&mut a, viewport, b"dispatch-driven\r\n", op())

+    let deadline = std::time::Instant::now() + Duration::from_secs(10);

+    let mut decoder = spt_net::net::attach::AttachDecoder::new();

+    while std::time::Instant::now() < deadline {

+                if stream_id == viewport =>

+                for rec in decoder.push(&bytes) {

+                    if let spt_net::net::attach::AttachRecord::Output { data_b64, .. } = rec {

+                            spt_daemon::msg::decode_bytes(&data_b64).expect("b64"),

+                if String::from_utf8_lossy(&echoed).contains("dispatch-driven") {

+        String::from_utf8_lossy(&echoed).contains("dispatch-driven"),

+        "the dispatcher served the viewport undriven; got {:?}",

+        String::from_utf8_lossy(&echoed)

+    stop.store(true, Ordering::Relaxed);

+// [int->REQ-REACH-1] a file fetch is served end to end by the dispatcher:

+// the wire names the gating endpoint; the file pumps back and commits.

+    // The served file lives under B's canonical transfer root.

+    let root = spt_store::xfer::transfers_dir();

+    std::fs::create_dir_all(root.join("notes")).unwrap();

+    std::fs::write(root.join("notes").join("d915.md"), b"fetched through dispatch").unwrap();

+    let mut a = connect_retry(&a_name);

+    let dest = dir.path().join("pulled.md");

+    let n = spt_daemon::xfer::fetch_file(

+        &dir.path().join("progress-a"),

+    assert_eq!(std::fs::read(&dest).unwrap(), b"fetched through dispatch");

+    stop.store(true, Ordering::Relaxed);


Bash

cargo test -p spt-daemon --test dispatch 2>&1 | grep -E "^error" -A 8 | head -30; cargo test -p spt-daemon --test dispatch 2>&1 | tail -12

Run extended dispatch suite


running 7 tests
test dispatcher_survives_an_unknown_stream ... ok
test dispatcher_serves_an_update_pull_undriven ... ok
test dispatcher_applies_a_notif_feed_undriven ... ok
test dispatcher_serves_a_sync_pull_undriven ... ok
test dispatcher_serves_a_remote_drive_attach_undriven ... ok
test dispatcher_serves_a_file_fetch_undriven ... ok
test dispatcher_funnels_wan_messages_undriven ... ok

test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.71s


D9-7: closeout sweep