Skip to content

RFC: add member indexing role for one-writer shared snapshots - #275

Open
hyein-cbio wants to merge 3 commits into
cocoindex-io:mainfrom
hyein-cbio:hyein-cbio/pencilfish
Open

hyein-cbio wants to merge 3 commits into
cocoindex-io:mainfrom
hyein-cbio:hyein-cbio/pencilfish

Conversation

@hyein-cbio

Copy link
Copy Markdown

Summary

This is an RFC / draft for an experimental one-writer team snapshot workflow.

Some teams do not want every developer machine to incrementally re-index. The intended model is:

  • one leader periodically runs incremental indexing
  • teammates (members) only search a published target_sqlite.db
  • members never write local or shared index state

The new process-local setting is:

COCOINDEX_CODE_INDEXING_ROLE=member   # default: leader

leader remains the default, so existing users are unchanged.

Why this is an RFC

CocoIndex does not recommend sharing live LMDB/SQLite databases over a mounted filesystem. This PR does not do that. Instead it keeps incremental state (cocoindex.db) on the leader and publishes only a completed SQLite snapshot.

Still, this is a new usage model:

  • SMB / mounted-FS atomic replacement is filesystem-dependent
  • members must use the same embedding provider, model, and query params as the leader
  • this is opt-in and still experimental

I would like maintainer feedback on whether this direction is acceptable before treating it as merge-ready.

Behavior

When COCOINDEX_CODE_INDEXING_ROLE=member:

  • MCP startup does not run background indexing
  • MCP search never refreshes or triggers load-time indexing, even if refresh_index=true
  • ccc index, ccc search --refresh, and ccc reset are rejected in that process
  • the published snapshot is opened read-only with a fresh connection per query
  • a matching COCOINDEX_CODE_DB_PATH_MAPPING is required; local index fallback is refused
  • a missing / unreadable snapshot returns an error instead of creating a local index

The role is process-local. Setting it in .mcp.json affects the MCP process only. The bundled SessionStart hook skips ccc index when the parent process already has COCOINDEX_CODE_INDEXING_ROLE=member.

Example member MCP config

{
  "mcpServers": {
    "cocoindex-code": {
      "command": "ccc",
      "args": ["mcp"],
      "env": {
        "COCOINDEX_CODE_INDEXING_ROLE": "member",
        "COCOINDEX_CODE_DB_PATH_MAPPING": "/Users/bob/src/myrepo=/Volumes/team/ccc-indexes/myrepo"
      }
    }
  }
}

Leader workflow: index locally, then sqlite3.Connection.backup() the completed target_sqlite.db and atomically os.replace() it onto the share. Do not publish cocoindex.db.

Non-goals

  • not a live multi-writer shared database
  • not a recommendation to run CocoIndex state on SMB
  • not a project-level setting in .cocoindex_code/settings.yml

Test plan

  • prek run --all-files
  • member MCP search does not call index
  • member ccc index / search --refresh / reset are rejected
  • member search uses a mapped snapshot and does not create local DBs
  • missing snapshot fails without creating files
  • stale same-version daemon without the member-readonly capability is rejected
  • real SMB atomic replace still needs field validation

Feedback especially welcome on:

  1. whether COCOINDEX_CODE_INDEXING_ROLE is the right knob
  2. whether member mode should refuse unmatched DB path mapping (current behavior)
  3. whether snapshot publish should stay documented-only or become a first-class ccc command

@hyein-cbio
hyein-cbio marked this pull request as ready for review August 19, 2026 20:06
@hyein-cbio

Copy link
Copy Markdown
Author

Added an embedding compatibility check in bd4be7d.

The previous read-only search path could accept a query embedding from a different model if its dimensions matched the stored vectors. This was reproduced with synthetic vectors against a real SQLite/vec0 table; it is not evidence of a production incident.

The follow-up adds:

  • A versioned embedding contract inside the SQLite snapshot, covering provider, model, and resolved indexing/query parameters.
  • Validation before query embedding, through the same read-only connection used for search.
  • A readiness flag cleared before indexing and restored only after successful completion with zero component errors.
  • A daemon capability check so an older same-version daemon cannot bypass validation.

Existing unlabelled indices remain usable by leaders. Member searches reject them and require a leader-local rebuild and republication. An incremental update alone does not establish the provenance of existing vectors. No automatic reset or rebuild is performed, and members remain read-only.

Validation: 87 focused tests and 3 existing member/readonly daemon tests passed, along with Ruff and mypy. An expanded run passed 293 tests and failed 5: four could not create Unix sockets in the execution environment, and one required the uninstalled optional sentence-transformers package. The checked prek run --all-files item in the PR description applies to the initial RFC, not this follow-up. Full socket/model end-to-end tests and SMB validation remain outstanding.

The [design note](https://github.com/hyein-cbio/cocoindex-code/blob/bd4be7d26c05d086a359c2799008fa52673ce62b/docs/pr275-embedding-compatibility-rfc.md) describes the metadata format and migration policy. Feedback would be helpful on whether rejecting legacy snapshots is appropriate, and whether query-only parameter changes should allow explicit recertification rather than a rebuild. Model revision and endpoint identity are not currently included in the contract.

Allow a per-process COCOINDEX_CODE_INDEXING_ROLE=member runtime to search
a published target_sqlite.db without starting or refreshing indexes.
Default remains leader so existing users are unchanged.

Member MCP sessions skip background indexing, ignore refresh_index, and
open the mapped snapshot read-only. Explicit ccc index, search --refresh,
and reset are rejected in that process. A matching DB path mapping is
required so members cannot fall back to a local index.

This is an RFC for a one-writer team snapshot workflow. Incremental
state stays on the leader; only a completed SQLite snapshot is published.
@hyein-cbio
hyein-cbio force-pushed the hyein-cbio/pencilfish branch from bd4be7d to b607638 Compare October 15, 2026 20:19

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant