zettelkasten.synapse.assemble¶
zettelkasten.synapse.assemble ¶
Wiring that makes the synapse navigation loop runnable against real stores.
This is the connective tissue between the three lower synapse workstreams and the navigation engine:
- :mod:
zettelkasten.synapse.substratesupplies the store adapters and the in-memory neighborhood assembler, but nothing composes those adapters from a scope. - :mod:
zettelkasten.synapse.navigationsupplies the deterministic executor, but its productionexpander/grounderseams default to NO-OPs — soexpandreveals no real neighbors andgroundre-fetches no real body.
This module closes both gaps WITHOUT touching either lower module's signatures:
- :func:
build_adapterscomposes a :class:~zettelkasten.synapse.substrate. MemoryAdapterplus one :class:~zettelkasten.synapse.substrate.ZettelAdapterper resolved source, REUSING :func:zettelkasten.synapse.retrieval.resolve_scope(and, through it, :func:~zettelkasten.synapse.retrieval.combined_grapher) exactly as thesynapse()read paths do — it never re-implements scope resolution. - :func:
adapter_expander/ :func:adapter_grounderturn a list of adapters into the injectable :data:~zettelkasten.synapse.navigation.Expander/ :data:~zettelkasten.synapse.navigation.Grounderseams the navigator calls, dispatching each request to the owning adapter by the node'sstore+source. - :func:
navigate_scopeties it all together: build adapters, assemble a neighborhood, and run the navigator to a :class:~zettelkasten.synapse. navigation.NavResult.
Design invariants (mirrors the rest of synapse):
- Strictly read-only. Nothing here writes
.memory/,.zettelkasten/, the link overlay, or.synapse/. The only side effects are store reads and the injected LLM call. - Pure + deterministic. Ordering follows the deterministic source order that
:func:
~zettelkasten.synapse.retrieval.resolve_scopereturns; adapter dispatch returns the first matching adapter. - No heavy top-level imports. Every heavy dependency (framing / retrieval / navigation / the graph loader) is imported function-locally, matching the substrate module's convention and avoiding import cycles.
deterministic_relevance_map ¶
deterministic_relevance_map(neighborhood: 'object', adapters: 'list[SourceAdapter]') -> dict[str, float]
Score assembled handles from stable adapter priority and retrieval rank.
assemble_neighborhood preserves adapter order and each adapter's returned
rank in the insertion order of neighborhood.nodes. This projection reuses
that deterministic order: a node's score is
1 / ((adapter_priority + 1) * (rank_within_adapter + 1)). Expanded nodes
arrive after retrieved seeds and therefore receive lower per-adapter rank.
No model-generated relevance enters the map.
Source code in zettelkasten/synapse/assemble.py
build_adapters ¶
build_adapters(projects: 'list[str] | None' = None, *, graphs_dir: 'object | None' = None, include_code: bool = False) -> 'list[SourceAdapter]'
Compose the store adapters for a cross-store scope (read-only).
Resolves the scope with :func:zettelkasten.synapse.retrieval.resolve_scope
— the SAME entry point the synapse() read paths use — and builds one
adapter per resolved source:
- a single :class:
~zettelkasten.synapse.substrate.MemoryAdapterfor the.memory/research tree (always included; the_memorysource thatresolve_scopeappends is skipped in the loop since this adapter already covers it); - one :class:
~zettelkasten.synapse.substrate.ZettelAdapterper resolved ZK box, whose lazygraph_providerroutes through the combined grapher thatresolve_scopereturns (so a cached/rebuilt graph is picked up fresh); - optionally a :class:
~zettelkasten.synapse.substrate.CodeAdapterwheninclude_codeis set.
projects=None uses the intersection config; an explicit list overrides it
([] = memory only). graphs_dir optionally points the ZK grapher at a
non-default store base (tests). A federated <repo_id>:<box> source IS
included: its identity is encoded into an address-safe source segment
(:func:_federated_source) so it is representable by the store:source:id
address scheme, while its graph_provider still resolves the REAL federated
graph through the combined grapher's <repo_id>:<box> routing.
Two soundness invariants are ENFORCED (not merely asserted) so no adapter can
ever be built with an address-invalid or colliding source:
- Validate-and-skip. The FINAL resolved
source(local or federated) is checked against the Address-forbidden character set (:func:_is_address_safe_source) right before the adapter is constructed; a failing source is omitted (continue) rather than emitted. This backstops federated identities carrying the reserved~delimiter or a forbidden char AND boundary-colon names (":box","repo:") that :func:~zettelkasten.federation.split_namespacedeclines and that would otherwise reachAddressverbatim and crash the read path. - No collision with the reserved federation namespace. A LOCAL box whose
name begins with the reserved
~fed~marker is skipped, so a local source can never equal a federated encoded source (which always carries that marker). This keeps_dispatch_adapterfrom silently shadowing a federated node with a same-named local box.
Returns the adapters in deterministic scope order (memory first).
Source code in zettelkasten/synapse/assemble.py
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | |
adapter_expander ¶
Build the navigator's :data:Expander seam over adapters.
The returned callable matches the navigation contract exactly — it is invoked
as expander(neighborhood, target) where target is a node token, and it
returns (neighbors, edges) for the navigator to merge. It locates the
target node in the current neighborhood, dispatches to the owning adapter by
the node's store + source, and returns that adapter's within-store
one-hop expansion. An unknown target (not in the neighborhood, or no owning
adapter) or a failing adapter yields ([], []) — a best-effort expansion
never sinks a run.
budget is the DECLARED assembly budget (default a fresh
:class:~zettelkasten.synapse.substrate.Budget): it is passed to
adapter.expand and, crucially, bounds each expand's fan-out — a single
call may reveal no more neighbors than the remaining room under
budget.max_nodes when set, truncating deterministically in adapter order.
None leaves fan-out count-uncapped; hop and navigator token bounds still
terminate the run. The navigator's _merge enforces the SAME optional
ceiling globally; this keeps the two growth paths consistent.
Source code in zettelkasten/synapse/assemble.py
adapter_grounder ¶
Build the navigator's :data:Grounder seam over adapters.
The returned callable matches the navigation contract exactly — it is invoked
as grounder(token) and returns the re-fetched :class:~zettelkasten.
synapse.substrate.Node (with its full, un-elided body) or None when the
token does not resolve. It parses the token to a MemDSL
:class:~zettelkasten.synapse.memdsl.schema.Address, dispatches to the owning
adapter by store + source, and returns adapter.get(address).
registry is accepted for API symmetry with the router's verifier registry
(so a future grounder can consult it) but is unused by the v1 seam, whose sole
job per the navigation contract is to re-fetch a node by token.
Source code in zettelkasten/synapse/assemble.py
navigate_scope ¶
navigate_scope(question: str, projects: 'list[str] | None' = None, *, llm: 'object | None' = None, budget: 'Budget | None' = None) -> 'NavResult'
Run the full grounded-navigation loop over a resolved scope.
A thin convenience that ties the pieces together: compose the store adapters
for projects (:func:build_adapters), assemble the in-memory neighborhood
for question (:func:~zettelkasten.synapse.substrate.assemble_neighborhood),
and run a :class:~zettelkasten.synapse.navigation.Navigator wired with the
adapter-backed :func:adapter_expander / :func:adapter_grounder seams.
llm defaults to :func:zettelkasten.synapse.navigation.default_llm (the
production model seam); a test injects a scripted stub. budget bounds the
neighborhood assembly (:class:~zettelkasten.synapse.substrate.Budget): its
hops>0 turns on pre-loop breadth-first expansion and its max_nodes is
threaded through as the navigator's hard in-loop ceiling.
Degrades GRACEFULLY: any assembly/model/navigation failure (a scope-resolve,
kglite, embedding-build, or LLM error) is caught and returned as a labeled
abstain :class:~zettelkasten.synapse.navigation.NavResult — never a raise —
mirroring the synapse(action="navigate") handler.
Returns the navigator's :class:~zettelkasten.synapse.navigation.NavResult.