| name | sr-semantic |
| description | Meaning-based (vector) search over your local warehouse of agentic-coding history. Reach for this when the question is about content or concepts you can describe but not name exactly — "conversations about flaky tests", "where did we debug the deadlock" — not exact ids, filters, or counts (that is sr-query). |
| enable-model-invocation | true |
sr-semantic
sr-semantic runs pure vector search against the stockroom warehouse — the single-file DuckDB database of your captured agentic coding harness history. It embeds your natural-language query and returns a ranked list of the most semantically similar messages. The surface is read-only by construction: you cannot corrupt anything by searching.
When to use sr-semantic
Reach for sr-semantic when the question is about meaning — content you can describe but not name exactly:
- "Find conversations about flaky test debugging."
- "Where did we work through the warehouse locking design?"
- Paraphrased or conceptual recall — the stored text won't contain your words verbatim, but it's about what you're asking.
Do not use sr-semantic for exact or structured lookups — a known session_id/message_id, filters, counts, GROUP BY, joins, date ranges. Those have a known shape and belong to the sr-query skill (raw read-only SQL). When you are not sure which is right, that judgement belongs to the sr-search skill.
Phrase the query as a description of the content you want, in natural language: a short phrase or sentence naming the topic, activity, or concept ("incremental re-embed of new content", "fixing the REUSE licensing layout"). Do not add any instruction preamble — the model's query prefix is applied automatically; hand-adding it would double it.
How to invoke the engine
stockroom semantic "how does the warehouse locking work"
If command -v stockroom fails, the machine isn't set up yet: tell the user to run the sr-initialize skill, and don't attempt any other invocation.
One runtime note: the model loader may print a Hugging Face hub notice / weight-loading progress to stderr — stdout stays clean for pipes; ignore the noise.
Output discipline: -k, --format, and --detail
The defaults are already safe for an agent, so reach for the flags only when a situation calls for it.
-k / --limit — result count, default 10
Lower it (-k 3) when you expect one obvious winner; raise it when you're casting wide before narrowing. Must be a positive integer.
--format — output shape, default tsv
| Value | Shape | Use it when |
|---|
tsv (default) | Header rank score harness role preview + tab-separated rows, no count trailer | Default. Stream-friendly for you and for unix pipes. |
json | A single {"results": [...]} object — each result additionally carries session_id and message_id and a numeric score | You need the hit's ids (e.g. for the full-text handoff below), a user asks for structured output, or you want jq. |
table | Column-aligned ASCII with a (N results) trailer | A user asks for something human-readable / a copy-paste command to look at. |
Lead with the default tsv. Offer --format table or --format json when the user asks for human-readable or structured output.
score is cosine similarity (higher = closer, ~0–1). Scores are relative quality within this corpus and query — read the previews to judge relevance; don't threshold on an absolute score.
--detail — preview width, default snippet
The preview/text field is truncated at read time so ranked previews can't flood your context. Truncation is display-only — full text is always retrievable.
| Value | Budget | Use it when |
|---|
compact | ~40 chars | Scanning many candidates cheaply before picking one. |
snippet (default) | ~120 chars | Default. Enough to recognize a hit without dumping it. |
full | unbounded, single-line | Almost never here — prefer the sr-query handoff below for whole text. |
raw | unbounded, exact whitespace | Exact stored text (newlines intact). Prefer with --format json; almost never on a large -k. |
An over-budget preview is elided with a marker reporting how many characters were hidden, e.g. …(+2539). That marker is your signal that more exists.
Guardrails
These are the failure modes this skill exists to prevent:
- Don't blow out your context. Never combine
--detail full/raw with a large -k — ten untruncated messages can be tens of thousands of characters. Scan at the default snippet (or compact), pick the hit you want, then fetch just that one message's whole text via the sr-query handoff: re-run with --format json to get the hit's message_id, then
stockroom query --format json --detail raw \
"SELECT text FROM messages WHERE message_id = '<message_id-from-the-json>'"
- It is read-only — never attempt writes. The surface only searches.
- Weak results for recent work usually mean stale embeddings, not absence. Semantic search only sees what has been embedded, and embeddings can lag ingestion. Before re-phrasing the same query in a loop, check coverage — and if it lags, suggest the user run the (incremental) embed pass:
stockroom query "SELECT
(SELECT count(DISTINCT owner_id) FROM embeddings WHERE owner_table = 'messages') AS embedded_messages,
(SELECT count(*) FROM messages) AS total_messages"
- Re-phrase, don't repeat. If results miss, one reworded query (different vocabulary for the same concept) is reasonable; more than that means the content likely isn't there or isn't embedded — switch strategy (
sr-query keyword ILIKE, or the coverage check above) instead of thrashing.
Handle errors without thrashing
Each failure is a clean stderr message + exit code — read it and take the matching action, don't loop:
| Message | Exit | What it means / next action |
|---|
error: empty query (…) | 2 | No search text was passed. Provide a query. |
error: --limit must be a positive integer | 2 | Bad -k. Fix the number. |
error: no warehouse found at … — run \stockroom ingest` first` | 1 | The warehouse hasn't been built. Tell the user to run stockroom ingest (or sr-initialize if the machine was never set up); don't retry. |
ModuleNotFoundError: No module named 'torch' | — | Environment problem, not a query problem (this surface needs torch at query time). Don't retry; tell the user to re-run sr-initialize to re-provision torch. |
Worked examples
All verified against a real warehouse.
stockroom semantic "how does the warehouse locking work"
stockroom semantic -k 3 "incremental re-embed of new content"
stockroom semantic --format json -k 2 "flock sidecar lock"
stockroom semantic --format table --detail compact -k 3 "REUSE licensing layout"
And the full-text handoff pair (scan semantically, then fetch one whole message with sr-query):
stockroom semantic --format json -k 5 "flock sidecar lock"
stockroom query --format json --detail raw \
"SELECT text FROM messages WHERE message_id = 'fcf35cbe-…#51'"
Relaying to a human
You are the tool's operator, not its display. Run the search, read the previews, and answer the user in natural language — cite the relevant hit(s), fetch full text only when the answer needs it. Don't paste raw tsv at a human unless they asked to see it. When they do want the raw output — or a command to run themselves — hand them a --format table (to read) or --format json (to process) variant.
Understanding the system
To understand why these contracts look the way they do — the torch contract, the embedding pipeline and its staleness model, read-only-by-construction, the truncation doctrine — read the shared system model: ../sr-search/references/system-model.md.