embedsync 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. embedsync-0.4.0/.env.example +2 -0
  2. embedsync-0.4.0/.github/workflows/ci.yml +11 -0
  3. embedsync-0.4.0/.gitignore +5 -0
  4. embedsync-0.4.0/CHANGELOG.md +29 -0
  5. embedsync-0.4.0/CONTRIBUTING.md +30 -0
  6. embedsync-0.4.0/LICENSE +21 -0
  7. embedsync-0.4.0/PKG-INFO +128 -0
  8. embedsync-0.4.0/README.md +105 -0
  9. embedsync-0.4.0/SECURITY.md +14 -0
  10. embedsync-0.4.0/compose.yaml +11 -0
  11. embedsync-0.4.0/docker/Dockerfile +8 -0
  12. embedsync-0.4.0/examples/docs/api.md +3 -0
  13. embedsync-0.4.0/examples/docs/getting-started.md +5 -0
  14. embedsync-0.4.0/plans/README.md +6 -0
  15. embedsync-0.4.0/plans/v0.1-completed-foundation.md +220 -0
  16. embedsync-0.4.0/plans/v0.2-next-phase-roadmap.md +366 -0
  17. embedsync-0.4.0/pyproject.toml +46 -0
  18. embedsync-0.4.0/src/embedsync/__init__.py +3 -0
  19. embedsync-0.4.0/src/embedsync/chunking.py +39 -0
  20. embedsync-0.4.0/src/embedsync/cli.py +111 -0
  21. embedsync-0.4.0/src/embedsync/config.py +11 -0
  22. embedsync-0.4.0/src/embedsync/destinations/__init__.py +0 -0
  23. embedsync-0.4.0/src/embedsync/destinations/jsonl.py +55 -0
  24. embedsync-0.4.0/src/embedsync/destinations/memory.py +52 -0
  25. embedsync-0.4.0/src/embedsync/embedders.py +88 -0
  26. embedsync-0.4.0/src/embedsync/sources/__init__.py +0 -0
  27. embedsync-0.4.0/src/embedsync/sources/local.py +34 -0
  28. embedsync-0.4.0/src/embedsync/state/__init__.py +0 -0
  29. embedsync-0.4.0/src/embedsync/state/store.py +93 -0
  30. embedsync-0.4.0/src/embedsync/sync/__init__.py +0 -0
  31. embedsync-0.4.0/src/embedsync/sync/engine.py +107 -0
  32. embedsync-0.4.0/tests/test_embedders.py +51 -0
  33. embedsync-0.4.0/tests/test_engine.py +48 -0
@@ -0,0 +1,2 @@
1
+ EMBEDSYNC_STATE_DB=.embedsync/state.db
2
+ EMBEDSYNC_LOG_LEVEL=INFO
@@ -0,0 +1,11 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v4
8
+ - uses: actions/setup-python@v5
9
+ with: {python-version: "3.12"}
10
+ - run: pip install -e ".[dev]"
11
+ - run: pytest tests/ -v
@@ -0,0 +1,5 @@
1
+ __pycache__/
2
+ .venv/
3
+ .pytest_cache/
4
+ .embedsync/
5
+ dist/
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ ## [0.4.0] - 2026-09-14
4
+
5
+ ### Added
6
+ - Ollama embedder via `--embedder ollama` or `--embedder ollama:<model>` (default model `nomic-embed-text`)
7
+
8
+ ## [0.3.0] - 2026-08-19
9
+
10
+ ### Added
11
+ - Chunk-level re-embed: unchanged chunks are skipped on document edit
12
+ - SQLite `chunks` table in the state store
13
+
14
+ ## [0.2.0] - 2026-08-19
15
+
16
+ ### Added
17
+ - Hash embedder for offline/CI runs (`--embedder hash`)
18
+ - Paragraph chunking with stable-ish chunk IDs
19
+ - JSONL destination (`--destination jsonl:/path`)
20
+ - Unchanged documents write 0 embeddings on the second run
21
+
22
+ ### Notes
23
+ - Real pgvector/Qdrant destinations are still open
24
+
25
+ ## [0.1.0] - 2026-08-18
26
+
27
+ ### Added
28
+ - Local markdown source, SQLite state store, sync plan engine
29
+ - Memory destination stub and dry-run mode
@@ -0,0 +1,30 @@
1
+ # Contributing
2
+
3
+ ## Running tests
4
+
5
+ Prefer Docker Compose:
6
+
7
+ ```bash
8
+ docker compose run --rm test
9
+ docker compose run --rm plan
10
+ ```
11
+
12
+ Locally:
13
+
14
+ ```bash
15
+ pip install -e ".[dev]"
16
+ pytest tests/ -v
17
+ embedsync plan examples/docs --state-db /tmp/embedsync-demo.db
18
+ embedsync run examples/docs --dry-run --state-db /tmp/embedsync-demo.db
19
+ ```
20
+
21
+ ## Pull requests
22
+
23
+ - Keep embedder changes covered by unit tests (mock Ollama where needed)
24
+ - Update README/CHANGELOG for new destinations or CLI flags
25
+ - Prefer small PRs with a clear test plan
26
+
27
+ ## Commit style
28
+
29
+ - Imperative subject line; mention the user-facing why when relevant
30
+ - Do not add AI co-author trailers (e.g. Co-authored-by: Cursor) to commits.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 embedsync contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.5
2
+ Name: embedsync
3
+ Version: 0.4.0
4
+ Summary: Incremental synchronization between source documents and vector indexes
5
+ Project-URL: Homepage, https://github.com/yashshah9/embedsync
6
+ Project-URL: Repository, https://github.com/yashshah9/embedsync
7
+ Project-URL: Issues, https://github.com/yashshah9/embedsync/issues
8
+ Author-email: Yash Shah <yash376351@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.11
12
+ Requires-Dist: click>=8.1
13
+ Requires-Dist: pydantic-settings>=2.2
14
+ Requires-Dist: pydantic>=2.6
15
+ Requires-Dist: pyyaml>=6.0
16
+ Requires-Dist: rich>=13.7
17
+ Requires-Dist: structlog>=24.1
18
+ Provides-Extra: dev
19
+ Requires-Dist: mypy>=1.9; extra == 'dev'
20
+ Requires-Dist: pytest>=8.0; extra == 'dev'
21
+ Requires-Dist: ruff>=0.4; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # embedsync
25
+
26
+ Incremental synchronization between **source documents** and **vector indexes** — detect changes, re-embed only deltas, and delete stale chunks.
27
+
28
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
29
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
30
+ [![CI](https://github.com/yashshah9/embedsync/actions/workflows/ci.yml/badge.svg)](https://github.com/yashshah9/embedsync/actions/workflows/ci.yml)
31
+
32
+ > **Status:** v0.4 — hash + Ollama embedders, paragraph chunks, JSONL destination, chunk-level re-embed.
33
+
34
+ ## 60-second try
35
+
36
+ ```bash
37
+ docker compose run --rm plan # plan sync for examples/docs
38
+ docker compose run --rm test # pytest
39
+ ```
40
+
41
+ ## Why this vs alternatives
42
+
43
+ | Approach | Strength | Gap |
44
+ |----------|----------|-----|
45
+ | **embedsync** | Content-hash deltas + pluggable embedders | Destinations still local (JSONL/memory) |
46
+ | Full re-embed pipelines | Simple mentally | Expensive; misses deletes |
47
+ | Framework ingestion (e.g. LlamaIndex) | Rich connectors | Change detection is DIY |
48
+ | One-off sync scripts | Fits one repo | No shared plan/state model |
49
+
50
+ ## Problem
51
+
52
+ RAG indexes rot when documents change. Full re-embeds are expensive and miss deletes. Every team rebuilds change detection from scratch.
53
+
54
+ ## Key features (v0.4)
55
+
56
+ - Content-hash change detection per document
57
+ - Sync plan: add / update / delete actions
58
+ - Hash embedder for offline/CI (`--embedder hash`)
59
+ - Ollama embedder (`--embedder ollama` or `ollama:nomic-embed-text`)
60
+ - JSONL or in-memory destination
61
+ - Unchanged docs/chunks skip re-embedding on the next run
62
+
63
+ ## Architecture
64
+
65
+ ```
66
+ embedsync run ./docs
67
+ ├── LocalFileSource
68
+ ├── StateStore (SQLite)
69
+ ├── plan_sync() → diff
70
+ └── Destination (Memory → pgvector next)
71
+ ```
72
+
73
+ ## Installation
74
+
75
+ ```bash
76
+ pip install embedsync
77
+ pip install -e ".[dev]"
78
+ ```
79
+
80
+ ## Usage
81
+
82
+ ```bash
83
+ embedsync health
84
+ embedsync plan examples/docs --state-db /tmp/embedsync-demo.db
85
+ embedsync run examples/docs --dry-run --state-db /tmp/embedsync-demo.db
86
+ embedsync run examples/docs --embedder hash --destination memory --state-db /tmp/embedsync-demo.db
87
+ embedsync run examples/docs --embedder hash --destination jsonl:/tmp/index.jsonl
88
+ # Requires a running Ollama with an embedding model:
89
+ embedsync run examples/docs --embedder ollama --destination jsonl:/tmp/index.jsonl
90
+ embedsync run examples/docs --embedder ollama:nomic-embed-text --destination memory
91
+ ```
92
+
93
+ ## Docker
94
+
95
+ ```bash
96
+ docker compose run --rm test
97
+ docker compose run --rm plan
98
+ ```
99
+
100
+ ## Configuration
101
+
102
+ | Variable | Default | Description |
103
+ |----------|---------|-------------|
104
+ | `EMBEDSYNC_STATE_DB` | `.embedsync/state.db` | State database path |
105
+ | `EMBEDSYNC_LOG_LEVEL` | `INFO` | Log level |
106
+
107
+ Ollama uses `OLLAMA_HOST` when set (otherwise the embedder default host).
108
+
109
+ ## Roadmap
110
+
111
+ - [x] Pluggable embedder protocol + hash backend
112
+ - [x] JSONL destination (local stand-in)
113
+ - [x] Chunk-level stable IDs across edits
114
+ - [x] Ollama embedder (`--embedder ollama`)
115
+ - [ ] pgvector and Qdrant destinations
116
+ - [ ] Notion and sitemap sources
117
+
118
+ ## License
119
+
120
+ MIT
121
+
122
+ ## Known limitations (v0.4)
123
+
124
+ - Hash embeddings are not semantic — use `--embedder ollama` for local semantic vectors
125
+ - JSONL is not a vector DB
126
+ - Local markdown files only
127
+ - Re-runs reuse `.embedsync/state.db`; pass `--state-db` for an isolated plan
128
+ - Ollama must already be running and have the embedding model pulled
@@ -0,0 +1,105 @@
1
+ # embedsync
2
+
3
+ Incremental synchronization between **source documents** and **vector indexes** — detect changes, re-embed only deltas, and delete stale chunks.
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
7
+ [![CI](https://github.com/yashshah9/embedsync/actions/workflows/ci.yml/badge.svg)](https://github.com/yashshah9/embedsync/actions/workflows/ci.yml)
8
+
9
+ > **Status:** v0.4 — hash + Ollama embedders, paragraph chunks, JSONL destination, chunk-level re-embed.
10
+
11
+ ## 60-second try
12
+
13
+ ```bash
14
+ docker compose run --rm plan # plan sync for examples/docs
15
+ docker compose run --rm test # pytest
16
+ ```
17
+
18
+ ## Why this vs alternatives
19
+
20
+ | Approach | Strength | Gap |
21
+ |----------|----------|-----|
22
+ | **embedsync** | Content-hash deltas + pluggable embedders | Destinations still local (JSONL/memory) |
23
+ | Full re-embed pipelines | Simple mentally | Expensive; misses deletes |
24
+ | Framework ingestion (e.g. LlamaIndex) | Rich connectors | Change detection is DIY |
25
+ | One-off sync scripts | Fits one repo | No shared plan/state model |
26
+
27
+ ## Problem
28
+
29
+ RAG indexes rot when documents change. Full re-embeds are expensive and miss deletes. Every team rebuilds change detection from scratch.
30
+
31
+ ## Key features (v0.4)
32
+
33
+ - Content-hash change detection per document
34
+ - Sync plan: add / update / delete actions
35
+ - Hash embedder for offline/CI (`--embedder hash`)
36
+ - Ollama embedder (`--embedder ollama` or `ollama:nomic-embed-text`)
37
+ - JSONL or in-memory destination
38
+ - Unchanged docs/chunks skip re-embedding on the next run
39
+
40
+ ## Architecture
41
+
42
+ ```
43
+ embedsync run ./docs
44
+ ├── LocalFileSource
45
+ ├── StateStore (SQLite)
46
+ ├── plan_sync() → diff
47
+ └── Destination (Memory → pgvector next)
48
+ ```
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pip install embedsync
54
+ pip install -e ".[dev]"
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ```bash
60
+ embedsync health
61
+ embedsync plan examples/docs --state-db /tmp/embedsync-demo.db
62
+ embedsync run examples/docs --dry-run --state-db /tmp/embedsync-demo.db
63
+ embedsync run examples/docs --embedder hash --destination memory --state-db /tmp/embedsync-demo.db
64
+ embedsync run examples/docs --embedder hash --destination jsonl:/tmp/index.jsonl
65
+ # Requires a running Ollama with an embedding model:
66
+ embedsync run examples/docs --embedder ollama --destination jsonl:/tmp/index.jsonl
67
+ embedsync run examples/docs --embedder ollama:nomic-embed-text --destination memory
68
+ ```
69
+
70
+ ## Docker
71
+
72
+ ```bash
73
+ docker compose run --rm test
74
+ docker compose run --rm plan
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ | Variable | Default | Description |
80
+ |----------|---------|-------------|
81
+ | `EMBEDSYNC_STATE_DB` | `.embedsync/state.db` | State database path |
82
+ | `EMBEDSYNC_LOG_LEVEL` | `INFO` | Log level |
83
+
84
+ Ollama uses `OLLAMA_HOST` when set (otherwise the embedder default host).
85
+
86
+ ## Roadmap
87
+
88
+ - [x] Pluggable embedder protocol + hash backend
89
+ - [x] JSONL destination (local stand-in)
90
+ - [x] Chunk-level stable IDs across edits
91
+ - [x] Ollama embedder (`--embedder ollama`)
92
+ - [ ] pgvector and Qdrant destinations
93
+ - [ ] Notion and sitemap sources
94
+
95
+ ## License
96
+
97
+ MIT
98
+
99
+ ## Known limitations (v0.4)
100
+
101
+ - Hash embeddings are not semantic — use `--embedder ollama` for local semantic vectors
102
+ - JSONL is not a vector DB
103
+ - Local markdown files only
104
+ - Re-runs reuse `.embedsync/state.db`; pass `--state-db` for an isolated plan
105
+ - Ollama must already be running and have the embedding model pulled
@@ -0,0 +1,14 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Email **yash376351@gmail.com** with the repo name, a short description, and steps to reproduce. Please do not open a public issue for exploitable findings until we have had a reasonable chance to respond.
6
+
7
+ ## Threat model (honest)
8
+
9
+ embedsync reads local documents, stores sync state in SQLite, and may call a local Ollama (or other configured) embedder.
10
+
11
+ - It is **not** a vector database security boundary.
12
+ - Document content is sent to the embedder you select (`hash` stays local; `ollama` hits your Ollama host).
13
+ - State DBs and JSONL destinations may contain document text and embeddings — treat them as sensitive if the source corpus is.
14
+ - There is no multi-tenant isolation; run one sync workspace per trust domain.
@@ -0,0 +1,11 @@
1
+ services:
2
+ test:
3
+ build: {context: ., dockerfile: docker/Dockerfile}
4
+ volumes: [".:/app"]
5
+ working_dir: /app
6
+ command: ["pytest", "tests/", "-v"]
7
+ plan:
8
+ build: {context: ., dockerfile: docker/Dockerfile}
9
+ volumes: [".:/app"]
10
+ working_dir: /app
11
+ command: ["embedsync", "plan", "examples/docs", "--state-db", "/tmp/embedsync-demo.db"]
@@ -0,0 +1,8 @@
1
+ FROM python:3.12-slim AS dev
2
+ WORKDIR /app
3
+ COPY pyproject.toml README.md ./
4
+ COPY src ./src
5
+ COPY examples ./examples
6
+ COPY tests ./tests
7
+ RUN pip install --no-cache-dir -e ".[dev]"
8
+ CMD ["pytest", "tests/", "-v"]
@@ -0,0 +1,3 @@
1
+ # API Reference
2
+
3
+ Endpoint documentation goes here.
@@ -0,0 +1,5 @@
1
+ # Getting Started
2
+
3
+ Welcome to our docs.
4
+
5
+ This is a sample document that will be indexed.
@@ -0,0 +1,6 @@
1
+ # embedsync — Planning Documents
2
+
3
+ | Document | Status | Description |
4
+ |----------|--------|-------------|
5
+ | [v0.1-completed-foundation.md](./v0.1-completed-foundation.md) | **Completed** | Initial foundation release |
6
+ | [v0.2-next-phase-roadmap.md](./v0.2-next-phase-roadmap.md) | **In Progress** | v0.2.0 shipped hash embedder + JSONL; pgvector still open |
@@ -0,0 +1,220 @@
1
+ # v0.1 Completed Foundation Plan — embedsync
2
+
3
+ **Status:** Completed
4
+ **Version:** 0.1.0
5
+ **Completed:** 2026-08-18
6
+ **Owner:** Yash Shah
7
+
8
+ ---
9
+
10
+ ## 1. Executive Summary
11
+
12
+ Deliver a **minimal viable open-source foundation** for incremental document-to-vector-index synchronization. v0.1 focuses on content-hash change detection, add/update/delete sync planning, SQLite state persistence, local markdown sources, and an in-memory destination with dry-run CLI — zero embedding API calls, zero vector DB infrastructure.
13
+
14
+ ---
15
+
16
+ ## 2. Problem Statement
17
+
18
+ - RAG indexes rot when source documents change; full re-embeds are expensive and miss deletes
19
+ - Every team rebuilds change detection, state tracking, and delta sync from scratch
20
+ - Existing tools either full-reindex or require heavyweight orchestration
21
+ - No standard CLI for "diff documents against last sync state and plan vector index mutations"
22
+
23
+ ---
24
+
25
+ ## 3. Goals (v0.1)
26
+
27
+ | # | Goal | Status |
28
+ |---|------|--------|
29
+ | G1 | Provide CLI installable Python package | Done |
30
+ | G2 | Content-hash change detection per document | Done |
31
+ | G3 | Sync plan: add / update / delete actions | Done |
32
+ | G4 | SQLite state store (no extra infrastructure) | Done |
33
+ | G5 | Local markdown file source | Done |
34
+ | G6 | In-memory destination for testing and dry-run | Done |
35
+ | G7 | `--dry-run` mode on `run` command | Done |
36
+ | G8 | Docker-based dev and test environment | Done |
37
+ | G9 | Contributor-ready repo (README, CI, LICENSE) | Done |
38
+
39
+ ## 4. Non-Goals (v0.1)
40
+
41
+ - Real vector database destinations (pgvector, Qdrant)
42
+ - Embedding API calls or pluggable embedders
43
+ - Chunk-level stable IDs across document edits
44
+ - Notion, sitemap, or remote document sources
45
+ - Chunk-level diff (document-level hash only)
46
+ - PyPI publish (deferred to v0.2)
47
+ - Web UI or hosted sync dashboard
48
+
49
+ ---
50
+
51
+ ## 5. Architecture Delivered
52
+
53
+ ```
54
+ CLI (Click + Rich)
55
+ ├── health → version + sanity check
56
+ ├── plan <source_dir> → plan_sync() → Rich table (dry)
57
+ └── run <source_dir> → execute_sync() with --dry-run flag
58
+
59
+ Sync Engine
60
+ ├── plan_sync() → diff source vs state → SyncPlan
61
+ └── execute_sync() → apply actions to destination + update state
62
+
63
+ Sources
64
+ └── LocalFileSource → glob **/*.md, relative path as doc_id
65
+
66
+ State
67
+ └── StateStore (SQLite) → documents(doc_id, content_hash, chunk_count)
68
+
69
+ Destinations
70
+ └── MemoryDestination → in-memory dict for testing/dry-run
71
+
72
+ Config
73
+ └── Settings (pydantic-settings) → EMBEDSYNC_* env vars
74
+ ```
75
+
76
+ ### Sync Flow
77
+
78
+ ```
79
+ LocalFileSource.list_documents()
80
+ └── for each doc:
81
+ ├── content_hash(SHA-256) vs StateStore.get()
82
+ ├── new doc → ADD
83
+ ├── hash changed → UPDATE
84
+ └── missing in source → DELETE (from state)
85
+ └── SyncPlan → Destination.apply() → StateStore upsert/delete
86
+ ```
87
+
88
+ ### Technology Choices
89
+
90
+ | Layer | Choice | Rationale |
91
+ |-------|--------|-----------|
92
+ | Language | Python 3.11+ | ML/RAG ecosystem familiarity |
93
+ | CLI | Click | Mature, composable subcommands |
94
+ | Terminal UX | Rich | Colored ADD/UPDATE/DELETE plan table |
95
+ | State | SQLite (stdlib) | Zero infra; portable single file |
96
+ | Hashing | SHA-256 (stdlib) | Fast, deterministic content fingerprint |
97
+ | Config | pydantic-settings | Env-based settings with `.env` support |
98
+ | Logging | structlog | Structured logs per sync action |
99
+ | Packaging | hatchling | Modern pyproject.toml builds |
100
+ | Quality | ruff, mypy, pytest | Standard Python OSS toolchain |
101
+
102
+ ---
103
+
104
+ ## 6. Deliverables Completed
105
+
106
+ ### 6.1 Source Code
107
+
108
+ - [x] `src/embedsync/cli.py` — `health`, `plan`, `run` commands
109
+ - [x] `src/embedsync/sync/engine.py` — `plan_sync()`, `execute_sync()`, `SyncPlan`
110
+ - [x] `src/embedsync/state/store.py` — `StateStore`, `DocumentState`, `content_hash()`
111
+ - [x] `src/embedsync/sources/local.py` — `LocalFileSource`, `SourceDocument`
112
+ - [x] `src/embedsync/destinations/memory.py` — `MemoryDestination`, `SyncAction`
113
+ - [x] `src/embedsync/config.py` — `Settings` with env prefix
114
+ - [x] `src/embedsync/__init__.py` — package version
115
+
116
+ ### 6.2 Sync Actions
117
+
118
+ | Action | Trigger | Destination Effect | State Effect |
119
+ |--------|---------|-------------------|--------------|
120
+ | ADD | doc_id not in state | Index doc (chunk count recorded) | upsert hash + chunk_count |
121
+ | UPDATE | content_hash changed | Re-index doc | upsert new hash |
122
+ | DELETE | doc_id in state but not in source | Remove from index | delete row |
123
+
124
+ ### 6.3 State Schema (SQLite)
125
+
126
+ | Column | Type | Description |
127
+ |--------|------|-------------|
128
+ | `doc_id` | TEXT PRIMARY KEY | Relative path from source root |
129
+ | `content_hash` | TEXT NOT NULL | SHA-256 hex digest of document content |
130
+ | `chunk_count` | INTEGER | Heuristic chunk count (not real embedding yet) |
131
+
132
+ ### 6.4 Chunk Heuristic (v0.1)
133
+
134
+ | Parameter | Value | Notes |
135
+ |-----------|-------|-------|
136
+ | Chunk size | 500 chars | Fixed heuristic; `ponytail:` comment in engine |
137
+ | Formula | `max(1, len(content) // 500)` | Placeholder until real chunker in v0.2 |
138
+
139
+ ### 6.5 Examples & Fixtures
140
+
141
+ - [x] `examples/docs/getting-started.md` — sample document
142
+ - [x] `examples/docs/api.md` — sample document
143
+
144
+ ### 6.6 Tests
145
+
146
+ - [x] `tests/test_engine.py` — plan detects 2 new docs; run indexes 2 docs
147
+ - [x] All tests passing in Docker (Python 3.12)
148
+
149
+ ### 6.7 DevOps & Documentation
150
+
151
+ - [x] `docker/Dockerfile` — multi-stage dev/runtime
152
+ - [x] `compose.yaml` — `test` and `plan` services
153
+ - [x] `.github/workflows/ci.yml` — ruff, mypy, pytest
154
+ - [x] `README.md`, `CHANGELOG.md`, `LICENSE`
155
+ - [x] `pyproject.toml` — hatchling build, `[project.scripts]` entry point
156
+
157
+ ---
158
+
159
+ ## 7. CLI Reference (v0.1)
160
+
161
+ | Command | Description | Key Options |
162
+ |---------|-------------|-------------|
163
+ | `embedsync health` | Print version and OK status | — |
164
+ | `embedsync plan <dir>` | Show ADD/UPDATE/DELETE plan without applying | `--state-db` |
165
+ | `embedsync run <dir>` | Execute sync plan against destination | `--dry-run`, `--state-db` |
166
+
167
+ ### Configuration
168
+
169
+ | Variable | Default | Description |
170
+ |----------|---------|-------------|
171
+ | `EMBEDSYNC_STATE_DB` | `.embedsync/state.db` | SQLite state database path |
172
+ | `EMBEDSYNC_LOG_LEVEL` | `INFO` | Log level |
173
+ | `EMBEDSYNC_DRY_RUN` | `false` | Default dry-run (CLI flag overrides) |
174
+
175
+ ---
176
+
177
+ ## 8. Acceptance Criteria (Met)
178
+
179
+ | # | Criterion | Verification |
180
+ |---|-----------|--------------|
181
+ | AC1 | `embedsync plan examples/docs` shows 2 ADD actions on first run | Manual + Docker `plan` |
182
+ | AC2 | `embedsync run examples/docs` indexes 2 documents in MemoryDestination | test_engine.py |
183
+ | AC3 | Second run with no changes produces 0 actions | Manual |
184
+ | AC4 | Content edit triggers UPDATE on subsequent plan | Manual |
185
+ | AC5 | Deleted file triggers DELETE on subsequent plan | Manual |
186
+ | AC6 | `--dry-run` applies no state or destination mutations | Manual |
187
+ | AC7 | `docker compose run --rm test` passes all tests | CI-ready |
188
+ | AC8 | No secrets or credentials in repository | .gitignore |
189
+
190
+ ---
191
+
192
+ ## 9. Known Limitations (v0.1)
193
+
194
+ - Local markdown files only (`**/*.md` glob)
195
+ - In-memory destination — no real vector DB writes
196
+ - Document-level hash only — any edit re-indexes entire document
197
+ - Naive fixed-size chunk count heuristic — no real chunking or embedding
198
+ - No embedding API calls — chunk_count is a placeholder integer
199
+ - No concurrent sync or locking — single-process CLI only
200
+ - No Notion, sitemap, or HTTP sources
201
+
202
+ ---
203
+
204
+ ## 10. Metrics at Completion
205
+
206
+ | Metric | Value |
207
+ |--------|-------|
208
+ | Source files | 7 Python modules |
209
+ | Test cases | 2 |
210
+ | CLI commands | 3 (`health`, `plan`, `run`) |
211
+ | Sync action types | 3 (add, update, delete) |
212
+ | Source types | 1 (local markdown) |
213
+ | Destination types | 1 (in-memory) |
214
+ | Docker services | 2 (`test`, `plan`) |
215
+
216
+ ---
217
+
218
+ ## 11. Sign-Off
219
+
220
+ v0.1 foundation is **complete and shippable** as an alpha OSS project. The sync engine, SQLite state, and dry-run CLI provide immediate value for planning document-to-index deltas without embedding cost. pgvector/Qdrant destinations, pluggable embedders, stable chunk IDs, and remote sources are deferred to v0.2 per roadmap.