Index Management
Intro
index-management is the read-side foundation. It walks the project's
context/ directory, parses every entity file, and writes a SQLite
database that other tools query. Its peer at Layer 0 is id-management
(the write side, which allocates new IDs). Both are foundational to
every entity-creating skill.
MCP server. This skill ships a self-contained MCP server at
mcp/server.py(PEP 723 script — requiresuvand Python ≥ 3.10 on PATH). Agent harnesses reach its tools by reading a single MCP config file at startup, so the contents ofmcp/mcp-config.jsonmust be merged into the harness's MCP config and placed at the harness-specific path before this skill is usable. If processkit was installed by an installer, that wiring is the installer's responsibility; if processkit was installed manually, the project owner must do it by hand.
Overview
What it provides
reindex()— rebuild the SQLite index from scratchquery_entities(kind?, state?, limit?)— list entities matching filtersget_entity(id)— fetch a single entity by IDsearch_entities(text, limit?)— FTS5 search across titles, bodies, specssemantic_status()— report semantic chunk/vector availabilitysemantic_search_entities(text, limit?)— sqlite-vec semantic search when availablehybrid_search_entities(text, limit?)— RRF over FTS5 + semantic results, with FTS-only fallbackquery_events(event_type?, subject?, actor?, limit?)— query the event loglist_errors()— files that failed to parse during the last reindexstats()— counts of entities/events/errors in the index
When to use
The MCP server runs continuously inside the dev container. Agents call it
whenever they need fast lookup. Other MCP servers (workitem-management,
decision-record, etc.) call reindex after writing a new entity to
keep the index fresh.
Where the database lives
<project-root>/context/.cache/processkit/index.sqlite. Gitignored.
Rebuildable from source files at any time via reindex().
Gotchas
Agent-specific failure modes — provider-neutral pause-and-self-check items:
- Grepping the filesystem instead of calling
query_entities. The index is faster, context-cheaper, and reflects parsed semantics (state, kind, links) that grep can't see. Reach forquery_entitiesfirst; fall back to grep only if the query you need isn't supported. - Trusting the index immediately after a hand-edit. When you edit
an entity file directly (bypassing the MCP write tools), the index
is stale until the next
reindex()call. Either go through the write tools, or callreindex()yourself before querying. - Forgetting to call
reindexafter writing a new entity. Every entity-creating MCP server should callindex_management.reindex(or the lighterupsert_entity) after a write. Not doing so means the next query won't see the just-created entity. - Treating index queries as transactional. The index is eventually consistent. A query immediately after a write may or may not return the new row depending on whether reindex completed. If you need strict consistency, reindex explicitly first.
- Trusting
list_errors()as proof of correctness. A clean errors table means parsing succeeded, not that the entities are semantically valid. Schema validation is a separate concern from parse success. - Using
search_entitiesfor exact-ID lookup.search_entitiesis ranked FTS5 search over parsed content, with a fallback substring search for invalid FTS syntax. Useget_entity(id)for exact lookup. - Assuming semantic search is always active. The semantic chunk table
is always rebuilt, but vector KNN requires the optional
sqlite-vecextension to load. Checksemantic_status(); usehybrid_search_entities()when you want an FTS-backed fallback. - Re-indexing inside a hot loop. A full reindex walks the whole
context/tree and is expensive. Batch your writes and reindex once at the end, not after every entity.
Full reference
Database schema
Three tables — see src/lib/processkit/index.py for the DDL.
entities(id, kind, api_version, path, storage_location, created, updated, title, state, labels_json, spec_json, body)
entities_fts(id, kind, state, title, body, spec_json)
semantic_chunks(rowid, chunk_id, entity_id, kind, state, path, ordinal, text)
entity_vec(embedding)
events(id, timestamp, event_type, actor, subject, subject_kind, summary, details_json, correlation_id, path)
errors(path, message)
spec_json holds the full spec block as JSON for queries we did not
anticipate. storage_location is NULL for hot-tree files and points at cold
archive payloads for archived entities. entities and events overlap for
LogEntry rows: a LogEntry appears in both, with the events table
denormalizing the event-specific fields for fast filtering.
entities_fts is a rebuildable SQLite FTS5 virtual table. It mirrors
the searchable fields from entities and is cleared/rebuilt during
reindex(). If a stripped-down SQLite build lacks FTS5, the index still
opens and search_entities falls back to the previous LIKE %text%
behaviour.
semantic_chunks is a rebuildable chunk table. When the optional
sqlite-vec package can be imported and loaded, entity_vec is created
as a vec0 virtual table containing deterministic local embeddings for
those chunks. The initial embedding strategy is provider-neutral
local-hashed-bag-of-words; it gives a stable offline baseline and a
storage contract for later provider/local embedding model upgrades.
Reindex strategy
reindex() is destructive and atomic — it deletes all rows and
re-inserts. For typical projects this is fast (sub-second). For very
large projects, an incremental update mode lands later (Phase 4+).
Errors table
Files that fail to parse get a row in the errors table instead of
crashing the reindex. list_errors() returns all such rows so the agent
can fix them. The errors table is cleared at the start of each reindex.
Tools that other servers call
workitem-management, decision-record, binding-management, and
event-log all call index_management.upsert_entity after writing a
new file. This keeps the index in sync without a full reindex on every
mutation. From an MCP-protocol perspective each server has its own
process — they communicate by sharing the same SQLite database file
(WAL mode would be enabled in a future release for concurrent writes).
Configuration
The index database path is configurable via:
processkit.toml[index] path = "..."(relative to project root)- The
PROCESSKIT_INDEX_DBenvironment variable - Default:
context/.cache/processkit/index.sqlite(gitignored cache)
Limitations at v0.3.0
- WAL mode enabled (v0.4.0+). Multiple readers and a single writer are safe; concurrent writes still serialize via WAL. Typical AI-assisted sessions are single-writer anyway.
- FTS5 search. Search uses SQLite FTS5 ranking when available and
falls back to
LIKE %text%for invalid FTS syntax or unsupported SQLite builds. - Optional sqlite-vec. Semantic search uses sqlite-vec only when the extension is installed and loadable; otherwise semantic search returns no vector results and hybrid search falls back to FTS5.
- No incremental indexing. Every reindex is a full sweep.