Add a memories table (survives index rebuilds) for agent-written conclusions with provenance (session, message range, project). The agent writes markdown files via Write tool (user-approved), then registers them via a --remember CodeAct script with remember(). Recall via memories() in --query scripts, filtered by project/session/time. Separates query (read-only, assertReadOnlySql) from remember (write) execution contexts in runtime.mjs.
4.5 KiB
Obelisk Retrieval Semantics
Read this before designing a non-trivial query. This is the query design frame;
pitfalls.md is only the debug checklist.
Four Principles
Scope First
Classify the user's request before choosing tools.
| User signal | Locator mode | Start with | Avoid first |
|---|---|---|---|
| project name/path, session, cwd, file, time range | scope | sessions(), exact SQL on project_path, sessionId, fileHistory() |
broad FTS |
| workflow, subagent, tool call, summary, edit | artifact | workflows(), subagents(), summaries(), tool_calls, tool_results |
all-session search |
| concept, conclusion, design history, vague memory | semantic | memories({ query }), search(), summaries, bounded facet sweep |
session dumps |
One-shot retrieval is not all-shot retrieval. A query script may perform
multiple steps, but the first locator should be the narrowest semantic fit. If a
scope locator finds the relevant project/session/file, do not also run broad FTS
unless scoped evidence is insufficient and query_plan says why.
Project-like fields are distinct:
sessions.project: stored Claude Code project slug.memories.project: stored project slug copied onto registered memory records.sessions.project_path: reconstructed absolute project path.messages.cwd: working directory at message time.- helper
project: SQLLIKEoversessions.project, not exact membership.
For exact project membership, use sql() with s.project = ? or
s.project_path = ?. Empty or tiny scoped results are valid results; do not
broaden unless the user asks or your query_plan explicitly marks a fallback.
Plan Before Probe
For conclusion, broad history, failure investigation, or file evolution tasks, prefer a retrieval script over interactive probing.
Good shape:
- locate candidates with scope/artifact/semantic locators;
- expand only selected hits;
- dedupe and group in the script;
- return compact evidence rows plus counts and limits.
If a second detail pass is needed, derive filters or facets from the first pass:
candidate sessions, discovered vocabulary, files, tools, timestamps, or
decisions. Prefer a learned faceted detail pass over LIMIT 25 session windows.
If vocabulary is still unclear, use a small filtered window and say so in
query_plan.
Structure Before Text
Use the database shape before asking the model to read text.
- Count and aggregate in SQL or JS (
GROUP BY,COUNT,MAX,ORDER BY,LIMIT). - Join metadata from the owner table instead of inventing fields.
- Project compact rows; do not return whole sessions, complete workflow trees, full raw messages, or entire tool results.
- Keep synthesis runtime JSON around 10k-12k chars when possible.
- For recent failures, aggregate by session/task and return sparse examples.
- For file evolution, filter
fileHistory()toEdit/Write, group by session or phase, and return short deltas.
Ordering and context are semantic:
sessions(),memories(),summaries(),workflows(), andfailures()are newest first.fileHistory()is oldest first.search().contextis temporal neighbors in one session, not causal context.context(uuid)andtrace(uuid)are for parent-chain/causal expansion.
Evidence Before Conclusion
Obelisk's raw session layer stores original structure, not precompiled claims: sessions, messages, summaries, tool calls/results, files, subagents, workflows, parent chains, and raw JSONL windows. The memory layer can store human-approved markdown conclusions, but treat them as prior notes to compare against raw evidence when correctness matters.
For semantic questions, build a task-local evidence view:
{
query_plan: { mode, scope, facets, limits },
prior_memories: [
{ id, path, session_id, created_at, summary }
],
evidence: [
{ type, id, session_id, timestamp, facet, snippet }
],
omitted: 0
}
Then synthesize the conclusion in the final answer. Do not pretend the raw evidence view is itself a stored Obelisk entity.
Text Search Semantics
search(text) passes text to SQLite FTS5 MATCH.
- Hyphens tokenize: for
workflow-script, use"workflow script"or SQLLIKEfor literal punctuation. - Special characters may produce FTS syntax errors; simplify or quote the FTS query under the same scope.
- Exact phrase, token search, and literal punctuation are different semantics.
- Results are ordered by
ORDER BY rank; lower rank sorts earlier. Prefer returned order over "closer to zero" comparisons.