Memory and search
Memory file mode and store mode and which one a desk uses, the search tool and its kinds, and what ticket-search embeddings are for, the providers and how to stop the Ollama probe.
01memory#
The conductor's memory has two modes. Which one a desk runs is decided when the server starts.
file mode
Set conductor.memory_dir (see desk-json) and that folder is the desk layer: plain .md files and a MEMORY.md index the conductor keeps by hand. The folder is checked, opened as a Memory, and memory_write, memory_edit and memory_delete work on it (;). A write with no scope goes to the desk folder, and a new file gets its line in MEMORY.md automatically. There is no recall tool in this mode: it is registered only with the store's tools (;). The search tool still finds these files under the memory kind.
store mode
With no conductor.memory_dir, the desk keeps its memory in smith's own store in the data dir. The store is one <name>.md per memory in a single folder, an INDEX.md smith rebuilds after every change, and a private keyword index over the text. Its tools replace the file tools of the same names:
memory_writetakes a name, description, type, body and links. The type is user, feedback, project or reference. Its result lists near_duplicate, dead_links, index_warning and date_warning when they apply.memory_editreplaces one exact piece of text that must appear exactly once;memory_deleteremoves a memory.recallsearches by BM25. A query term the store has never seen also matches terms that share a prefix of four or more letters, or are one edit away. It returns up to 5 hits by default and at most 20, each with its name, description, type, links, body and layer, area hits first, then project, then desk. The project's manager can passscopeasarea:<name>to search one area.
Edit and delete look in the chat's area, then its project, then the desk, in both modes. See memory-layers for the layers.
If a desk has neither memory_dir nor a data dir, neither branch registers memory tools.
02search#
The indexer is one service with a source for each kind of thing the desk holds, each kind in an index of its own, so what one kind holds is never found through another. A sync keeps only what changed, by content hash. Search is BM25, fused with cosine similarity on the local embedding model by reciprocal rank when a model is set. The sources:
| kind | what is indexed | source |
|---|---|---|
| chats | each chat's title and the text of its messages | |
| runs | the runs page rows: title, status, failure and the digest's ticket, template and failure. Listed again when older than 15 seconds | , 56-73 |
| files | a project's files. A git checkout gives every file .gitignore doesn't hide; any other project gives markdown, text, pdf, docx, xlsx and pptx text. A file over 1 MiB, or a binary, is left out, and one project adds at most 20000 files |
|
| assets, skills | assets, and the assets that are skills | |
| memory | the conductor's memories, from the store or the memory dir |
Tickets, learnings and comments are not in the indexer. They stay on the tracker's own search, and the search tool asks the tracker for them.
the search tool
search is read-only and takes query, kinds and project:
queryis required; an empty one is refused.kindslimits the search to some of: tickets, learnings, comments, runs, chats, assets, skills, files, memory. All of them by default. A kind outside the list is refused with the list in the message.projectis a project's name to search only that project, everywhere by default. An unknown name is refused.
It returns at most 30 hits, each with its kind, title, snippet, project, link and score, best first, and any kind that couldn't answer under errors. Memory text is found only in memory hits. For what embeddings add to ticket search, see embeddings below.
03embeddings#
What for: semantic ticket search in the tickets service (internal/tickets/svc, internal/tickets/embed). Without embeddings, search is keyword-only (BM25).
in the desk: search.runs_on
The current way to choose is desk.json search.runs_on: built_in (the default) or ollama. It is global and live-reloaded. ollama_url defaults to http://localhost:11434 and ollama_model to bge-m3 (withDefaults); values are validated at. See desk.json search.
- The built-in model is
<data dir>/models/bge-small-en-v1.5(prepareSearchModel, internal/cli/desk_search_model.go). It is copied once from the old CLI folder throughembed.CopyVerified(internal/cli/desk_legacy_model.go). - The local tracker shares the desk's model and ignores
embed_provider. SearchRunsOn.Applyprobes Ollama only whenruns_onisollama; a failure leaves search keyword-only. So in the desk,runs_on: built_in(or unset) is what stops the probe.
the smith serve and standalone tracker path: embed_provider
Providers (embed_provider; internal/tickets/embed/provider.go accepts only local, ollama, openai). Defaults are in internal/tickets/config/config.go; an example is in examples/tickets/config.yaml:
local(default): bge-small-en-v1.5 in-process, fetched once withsmith tickets embed fetch. Until fetched, search is keyword-only.ollama:ollama_url(defaulthttp://localhost:11434) andollama_model(defaultbge-m3).openai: needsopenai_api_key.
the built-in tracker never dials Ollama
The desk's built-in tracker takes localConfig (internal/trackers/local.go). It calls config.Load(), then sets EmbedProvider to local and blanks OllamaURL, OllamaModel and OpenAIKey. The tickets config's embed keys and per-project overrides are ignored there. Its model is <core data dir>/models/bge-small-en-v1.5 (internal/cli/desk.go). When the model is missing, search stays keyword-only.
the Ollama probe
Only a standalone tickets tracker (for example smith tickets or smith serve) probes. It does so when its config has embed_provider: ollama, or when a project record overrides it (internal/tickets/svc/service.go).
If Ollama is unreachable at boot, the service starts keyword-only and retryDeferredEmbed retries in the background with backoff 1s, 2s, 4s, 8s, 16s, 32s, then 60s. It logs one WARN, then DEBUG lines. Mounts catch up when Ollama answers; the unmounted default index stays keyword-only until restart.
turning the probe off
- In the tickets config file
~/.tickets_please/config.yaml(configPath), setembed_provider: local, or remove the key (the default islocal). - Check the environment:
config.Loadalso reads an environment variable namedEMBED_PROVIDER(the lowercased name must match a config key), which overrides the file. Unset it. - Set any project record's
embed_provideroverride back to local. - Restart that tracker.
No setting turns embeddings off entirely. Only local, ollama and openai are accepted. Keyword-only search happens only as a fallback (local model not fetched, or provider unreachable).