Skip to content
Back to skills

Memory Search Advanced

ASecurity

Use when running thrum memory search with RAG (--near), composing complex predicate stacks, paginating large result sets, interpreting MEM-001..010 hint codes, or diagnosing embedding adapter behavior. Loads three-vector ranking semantics, length-bias caveat, and the full hint catalog.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
ai-agentsrustgobashexpress

Works with

  • cli

Security analysis

A100/100

Scanned November 7, 2026

npx -y skills add thrum-team/thrum-pro --skill memory-search-advanced --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Memory Search Advanced?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Memory Search Advanced
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/thrum-team-memory-search-advanced/badge)](https://www.skillsdirectory.com/skills/thrum-team-memory-search-advanced)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: memory-search-advanced
description: "Use when running thrum memory search with RAG (--near), composing complex predicate stacks, paginating large result sets, interpreting MEM-001..010 hint codes, or diagnosing embedding adapter behavior. Loads three-vector ranking semantics, length-bias caveat, and the full hint catalog."
allowed-tools: "Bash(thrum memory:*),Bash(thrum config:*)"
version: "0.11.0"
license: "Apache-2.0"
---

# memory-search-advanced — RAG, hint codes, complex predicates

Load this sub-skill from `thrum-memory` (umbrella) for predicate needs beyond
tag + kind + recency filtering.

## Predicate surface (shipped)

`thrum memory search` combines all predicates with AND:

```text
Query:
  positional args — FTS query text; `thrum memory search "<topic>"` is
  equivalent to --grep "<topic>" (multiple args join with single spaces;
  giving both positional and --grep is refused as ambiguous)

Identity / structure:
  --kind <kind>, --tag <tag> (SINGLE tag per query), --scope project|agent|role,
  --status <status> ("*" for any; default active)

Text:
  --grep <terms> (full-text MATCH over title + body_oneline/short/full;
  terms matched literally — punctuation is not FTS5 operators)

Semantic (when embedding adapter enabled):
  --near "<text>"

Time:
  --since 7d | <Go duration> | <RFC3339>

Sort & limit:
  --sort created_at_desc|updated_at_desc|relevance|pinned_first
  --limit 10 (default)
  --offset N
```

Output formats: `table` (default), `ids`, `oneline`, `short`, `full`, `json`.

Tag-set OR is NOT expressible in one query — `--tag` takes a single tag. For OR
semantics, run one search per tag and merge.

## DESIGNED — NOT YET SHIPPED (do not use)

The original memory-search design specifies a wider predicate surface that the
shipped CLI does not implement. The flags below DO NOT EXIST today — the CLI
rejects them with `unknown flag`. They are recorded here as the spec diff for
the eventual search-surface implementer, NOT as usable commands:

```text
--id, --kinds (multi-kind), --subkind, --agent / --author, --pinned,
--tags-any (tag-set OR),
--since-created, --since-updated, --until,
--parent <id>, --supersedes <id>, --superseded-by <id>,
--related-to <id>, --derived-from <id>,
--grep-field <oneline|short|full|title>, --regex,
--near-id <id>, --near-limit N
```

Workarounds with the shipped surface: time filtering = `--since` only (no
created-vs-updated split); per-field grep = not available (grep spans title +
all three zooms); edge predicates = not available (edge reads are the
`memory.listByEdge` RPC, no CLI); `--pinned` = `--tag pinned` (pinned is a
tag).

## Pagination patterns

Default `--limit 10` keeps context bounded. Walk a large set:

```bash
thrum memory search --kind session_summary --since 30d --limit 25 --offset 0
thrum memory search --kind session_summary --since 30d --limit 25 --offset 25
```

For counting: `thrum memory count` is the cheap O(1) counter, but it counts by
`--agent` / `--role` only — it accepts NO kind/time filters. To count a filtered
set, read the `total` field from the JSON response (a separate COUNT query,
computed before LIMIT applies):

```bash
thrum memory count --role researcher                  # cheap, coarse
thrum memory search --kind session_summary --since 30d \
  --limit 1 --format json | jq '.total'               # filtered count
```

Do NOT count via `--limit 0 --format ids | wc -l` — the `--limit 0`
streaming/export reservation is currently non-functional (the CLI drops
`limit=0` from the RPC params and the daemon clamps the absent limit to 10, so
output caps at 10 rows regardless of the real match count).

## RAG semantics (`--near`)

`--near "<text>"` runs semantic search against memory embeddings. Three
per-memory vectors are stored — one per zoom level, embedding the title-prefixed
string:

- `<title> — <body_oneline>`
- `<title> — <body_short>`
- `<title> — <body_full>`

Ranking is `score = max(sim_oneline, sim_short, sim_full)`. Each result carries
`matched_zoom` so the CLI can render the zoom level that actually matched.
Per-zoom embeddings let topical queries dock against `oneline` while
deep-content queries dock against `full`.

### Length-bias caveat — read before trusting `--near` results

Title-prefixed `oneline` strings (~40-50 chars) and `body_full` (up to 256 KB)
embed in different subspaces of `granite-30m-english`'s 384-dim space. **Short
queries can systematically match `oneline` over `full` for length-similarity
reasons rather than semantic-relevance reasons.** This is a real bias inherited
from the embedding model.

Detection and recovery (manual in v1; a future MEM-011+ zoom-divergence
detector is reserved for this case; the shipped MEM-008 is the
embed-enqueue-failed warning, not a zoom-divergence detector):

- Inspect `matched_zoom` in each result. If short queries return rows dominated
  by `matched_zoom=oneline`, the ranking may have been misled.
- Re-run with a longer query phrasing.
- Add `--kind <X>` to narrow before semantic re-rank.
- Inspect the full body manually, following the shared 3-step read pattern
  (`thrum-memory`): triage with `thrum memory show <id> --zoom short`
  first, then fetch with `thrum memory show <id> --zoom full` once confirmed.

Mitigations weighed and deferred (don't reach for them yet): weighted
combination, cross-encoder re-rank, per-kind length normalization. v1 ships
max-of-three; tune later only if `matched_zoom` diagnostics surface systematic
problems.

## Hint codes (MEM-001..010)

Hints render as a `hints` array in `--json` mode and as inline notices in human
mode. Suppress everywhere with `THRUM_NO_HINTS=1`. Severity is `warn` for
blocking-shaped advice, `info` for nudges.

| Code      | Trigger                                                           | What it tells you                                                    |
| --------- | ----------------------------------------------------------------- | -------------------------------------------------------------------- |
| `MEM-001` | search returned >50 matches                                       | Narrow with `--kind` / `--tag` / `--since`                           |
| `MEM-002` | `--near` supplied without `--kind`                                | Add `--kind` to narrow before semantic re-rank (deferred)            |
| `MEM-003` | empty result with two or more narrowing predicates                | Drop the most-restrictive flag                                       |
| `MEM-004` | `--near` with embedding adapter disabled                          | Hard refusal — enable the adapter (see setup below)                  |
| `MEM-005` | `--grep` matched nothing AND field is empty for most rows of kind | Broaden: drop `--kind`                                               |
| `MEM-006` | `--near` found rows with `embed_status=pending` → FTS fallback    | Wait for backfill or run `memory.embed.rebuild` (deferred)           |
| `MEM-007` | `--near` skipped rows whose `embedding_model` ≠ configured model  | Run backfill to migrate vectors (deferred)                           |
| `MEM-008` | `memory.create` enqueue to embed worker failed                    | Best-effort; the worker will retry via `memory.embed.rebuild`        |
| `MEM-009` | `memory.edit` projection detected concurrent edit; LWW loser      | Loser's value is preserved in `memory.history` (deferred surfacing)  |
| `MEM-010` | `memory.create` omitted `body_short` or `body_full`               | Three-zoom retrieval works better with all three; non-blocking nudge |

**Deferred triggers:** MEM-002, MEM-006, MEM-007 are registered today but the
emission path lights up in a future epic (`--near` adapter wiring +
multi-model support). MEM-009 emission lights up when LWW-loser surfacing
lands in the projection response. The codes are stable; only the trigger
conditions are in flight.

**Hard refusal:** MEM-004 is the only hint with `AllowForce=false`. There is no
recovery flag — the adapter is either present or absent.

## Embedding adapter awareness

`--near` only works when the embedding adapter is enabled:

```bash
thrum config set llm.embedding.enabled true
thrum config set llm.embedding.provider ollama --force
thrum config set llm.embedding.base_url http://localhost:11434 --force
thrum config set llm.embedding.model "granite-embedding:30m-english"
```

Embedding work is queued on write (OS-nice `+10`, single-flight per memory_id,
exponential backoff). Writes never block on embedding. Searches degrade
gracefully to FTS for not-yet-embedded rows and surface MEM-006 when the
degradation happens.

Edits mark only affected zoom-levels as `embed_status=stale`; tag-only edits
re-embed nothing, title-only edits re-embed all three. Old vectors stay
queryable during the staleness window.

Cold-start backfill runs once on the first daemon boot with the adapter enabled.
Disable with `embedding.backfill_on_boot: false`; rate-limit via
`embedding.max_per_minute` (default 60).

## Composing complex predicates

Stack predicates left-to-right; they AND. Use `--scope` only when you
specifically want to filter by assembly scope (it is NOT visibility — see
umbrella skill).

Example: every active arch decision tagged `substrate`, edited recently,
semantically related to "RPC routing" (tag-set OR = one query per tag):

```bash
thrum memory search \
  --kind arch_decision \
  --tag substrate \
  --status active \
  --since 14d \
  --near "RPC routing" \
  --limit 25
```

Example: every superseded session summary that touched a given epic, paged:

```bash
thrum memory search \
  --kind session_summary \
  --status superseded \
  --tag epic-thrum-abc \
  --sort updated_at_desc \
  --limit 50 --offset 0
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.