1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! Daemon-forwarding half of [`CommsClient`](super::client::CommsClient): the write/read RPCs a
//! `daemon_writer` serve ships to the daemon (the machine's sole fjall writer) rather than running
//! in-process — rescan, CORE memory, PROPOSAL governance, precise resolved-refs, and git-history.
//!
//! Split out of `client.rs` to keep it under the 1000-line `rust-max-lines` cap. These are a second
//! `impl CommsClient` block; the request/response correlation core stays in `client.rs`.
use std::path::PathBuf;
use super::client::{CommsClient, CommsClientError, RescanReport};
use super::protocol::{CommsRequest, CommsResponse};
impl CommsClient {
/// Ask the daemon (the machine's sole fjall writer) to scan or rescan a workspace. Front-ends
/// forward their writes here so concurrent read-only sessions never contend for the index lock.
/// A non-empty `paths` (with `full == false`) drives an incremental rescan; otherwise the whole
/// working tree is scanned. `embed` requests an
/// [`EmbedMode::Inline`](crate::scanner::EmbedMode::Inline) vector-fill pass (documents + code
/// chunks); `false` is the fast `Deferred` code-map pass. Idempotent, so the transparent
/// reconnect-and-retry is replay-safe.
pub async fn rescan(
&mut self,
root: PathBuf,
paths: Option<Vec<PathBuf>>,
full: bool,
embed: bool,
) -> Result<RescanReport, CommsClientError> {
match self
.request(CommsRequest::Rescan {
root,
paths,
full,
embed,
})
.await?
{
CommsResponse::Rescanned {
scanned,
updated,
docs_indexed,
removed,
elapsed_ms,
} => Ok(RescanReport {
scanned,
updated,
docs_indexed,
removed,
elapsed_ms,
}),
other => Err(self.shape_err(other, "rescan")),
}
}
/// Forward a CORE memory operation to the daemon (the sole fjall writer). A `daemon_writer`
/// serve resolves the namespace + scope and ships the op here; the vector (LanceDB) half stays
/// serve-side. Idempotent for get/list/delete; `put` is a preserving RMW, so a replayed retry is
/// safe.
#[cfg(feature = "memory")]
pub async fn memory_op(
&mut self,
root: PathBuf,
scope: String,
op: crate::comms::memory_proto::MemoryOp,
) -> Result<crate::comms::memory_proto::MemoryOutcome, CommsClientError> {
match self.request(CommsRequest::Memory { root, scope, op }).await? {
CommsResponse::Memory(outcome) => Ok(outcome),
other => Err(self.shape_err(other, "memory_op")),
}
}
/// Forward a PROPOSAL governance operation to the daemon (the sole fjall writer). A
/// `daemon_writer` serve does the git-log mining / audit verdict / LanceDB embed on its side and
/// ships only the fjall reads/writes here. Idempotent for list/get; reject + promote are
/// terminal writes and mine-apply is tombstone-guarded, so a replayed retry is safe.
#[cfg(feature = "memory")]
pub async fn governance_op(
&mut self,
root: PathBuf,
scope: String,
op: crate::comms::proposals_proto::GovernanceOp,
) -> Result<crate::comms::proposals_proto::GovernanceOutcome, CommsClientError> {
match self.request(CommsRequest::Governance { root, scope, op }).await? {
CommsResponse::Governance(outcome) => Ok(outcome),
other => Err(self.shape_err(other, "governance_op")),
}
}
/// Forward a precise resolved-reference read to the daemon (the sole fjall writer, holding the
/// cross-file `refs_by_def` / `refs_by_path` index a `daemon_writer` serve cannot see). Backs
/// the precise cross-file `find_callers` / `goto_definition` path. A pure read, so the
/// transparent reconnect-and-retry is replay-safe.
pub async fn resolved_refs(
&mut self,
root: PathBuf,
query: crate::comms::resolved_proto::ResolvedRefQuery,
) -> Result<crate::comms::resolved_proto::ResolvedRefResult, CommsClientError> {
match self.request(CommsRequest::ResolvedRefs { root, query }).await? {
CommsResponse::ResolvedRefs(result) => Ok(result),
other => Err(self.shape_err(other, "resolved_refs")),
}
}
/// Forward the code-search keyword (BM25) + exact (symbol-name) lanes to the daemon — the sole
/// fjall writer, and therefore the only process that can read the BM25 postings and
/// `symbols_by_name`. Backs `code` mode `semantic` on a reader session, where both lanes would
/// otherwise return nothing. A pure read, so the transparent reconnect-and-retry is replay-safe.
pub async fn code_search_lanes(
&mut self,
root: PathBuf,
query: crate::comms::code_search_proto::CodeSearchLaneQuery,
) -> Result<crate::comms::code_search_proto::CodeSearchLaneResult, CommsClientError> {
match self.request(CommsRequest::CodeSearchLanes { root, query }).await? {
CommsResponse::CodeSearchLanes(result) => Ok(result),
other => Err(self.shape_err(other, "code_search_lanes")),
}
}
/// Forward a git-history operation to the daemon — the sole holder of `git-history.fjall/`
/// (fjall's directory lock is exclusive, so no front-end may open it). Backs both the index
/// BUILD a `daemon_writer` serve requests at startup instead of running it in-process, and the
/// history reads its `recent_changes` / `commits_touching` / `hot_files` / `search_git_history`
/// tools would otherwise have to degrade to a live walk.
///
/// Every op is idempotent — the reads are pure and `Sync` is freshness-checked (a replayed sync
/// against an up-to-date index is a no-op) — so the transparent reconnect-and-retry is safe.
pub async fn git_history(
&mut self,
root: PathBuf,
op: crate::git_history::proto::GitHistoryOp,
) -> Result<crate::git_history::proto::GitHistoryReply, CommsClientError> {
match self.request(CommsRequest::GitHistory { root, op }).await? {
CommsResponse::GitHistory(reply) => Ok(reply),
other => Err(self.shape_err(other, "git_history")),
}
}
}