Yash030 commited on
Commit
5cd3cca
Β·
1 Parent(s): 1846fdf

Added new docs

Browse files
.kiro/skills/to-tickets/SKILL.md ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: to-tickets
3
+ description: Break a plan, spec, or the current conversation into a set of tracer-bullet tickets, each declaring its blocking edges, published to the configured tracker β€” edges as text in one file per ticket locally, or native blocking links on a real tracker.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ Break a plan, spec, or conversation into a set of tickets β€” tracer-bullet vertical slices, each declaring the tickets that block it.
8
+ The issue tracker and triage label vocabulary should have been provided to you β€” run /setup-matt-pocock-skills if not.
9
+
10
+ ## Process
11
+
12
+ ### 1. Gather context
13
+ Work from whatever is already in the conversation context. If the user passes a reference (a spec path, an issue number or URL) as an argument, fetch it and read its full body and comments.
14
+
15
+ ### 2. Explore the codebase (optional)
16
+ If you have not already explored the codebase, do so to understand the current state of the code. Ticket titles and descriptions should use the project's domain glossary vocabulary, and respect ADRs in the area you're touching.
17
+
18
+ Look for opportunities to prefactor the code to make the implementation easier. "Make the change easy, then make the easy change."
19
+
20
+ ### 3. Draft vertical slices
21
+ Break the work into tracer bullet tickets.
22
+
23
+ - Each slice cuts a narrow but COMPLETE path through every layer (schema, API, UI, tests) β€” vertical, NOT a horizontal slice of one layer
24
+ - A completed slice is demoable or verifiable on its own
25
+ - Each slice is sized to fit in a single fresh context window
26
+ - Any prefactoring should be done first
27
+
28
+ Give each ticket its blocking edges β€” the other tickets that must complete before it can start. A ticket with no blockers can start immediately.
29
+
30
+ Wide refactors are the exception to vertical slicing. A wide refactor is one mechanical change β€” rename a column, retype a shared symbol β€” whose blast radius fans across the whole codebase, so a single edit breaks thousands of call sites at once and no vertical slice can land green. Don't force it into a tracer bullet; sequence it as expand–contract. First expand: add the new form beside the old so nothing breaks. Then migrate the call sites over in batches sized by blast radius (per package, per directory), each batch its own ticket blocked by the expand, keeping CI green batch to batch because the old form still exists. Finally contract: delete the old form once no caller remains, in a ticket blocked by every migrate batch. When even the batches can't stay green alone, keep the sequence but let them share an integration branch that all block a final integrate-and-verify ticket β€” green is promised only there.
31
+
32
+ ### 4. Quiz the user
33
+ Present the proposed breakdown as a numbered list. For each ticket, show:
34
+
35
+ - **Title:** short descriptive name
36
+ - **Blocked by:** which other tickets (if any) must complete first
37
+ - **What it delivers:** the end-to-end behaviour this ticket makes work
38
+
39
+ Ask the user:
40
+
41
+ - Does the granularity feel right? (too coarse / too fine)
42
+ - Are the blocking edges correct β€” does each ticket only depend on tickets that genuinely gate it?
43
+ - Should any tickets be merged or split further?
44
+
45
+ Iterate until the user approves the breakdown.
46
+
47
+ ### 5. Publish the tickets to the configured tracker
48
+ Publish the approved tickets. How depends on the tracker /setup-matt-pocock-skills configured β€” the tickets are the same either way, only the shape of the blocking edges changes:
49
+
50
+ - **Local files** β†’ write one file per ticket under `.scratch/<feature-slug>/issues/<NN>-<slug>.md`, numbered from 01 in dependency order (blockers first). Each file's "Blocked by" lists the numbers/titles it depends on. Use the per-ticket file template below β€” one ticket per file, never a single combined file.
51
+ - **A real issue tracker (GitHub, Linear, …)** β†’ publish one issue per ticket in dependency order (blockers first) so each ticket's blocking edges can reference real identifiers. Use the platform's native blocking / sub-issue relationship where it has one; otherwise set each ticket's "Blocked by" to the blocking issues. Apply the ready-for-agent triage label unless instructed otherwise β€” the tickets are agent-grabbable by construction.
52
+
53
+ Work the frontier: any ticket whose blockers are all done. For a purely linear chain that means top to bottom.
54
+
55
+ Do NOT close or modify any parent issue.
56
+
57
+ ---
58
+
59
+ ## Per-ticket file template (local files)
60
+
61
+ **What to build:** the end-to-end behaviour this ticket makes work, from the user's perspective β€” not a layer-by-layer implementation list.
62
+ **Blocked by:** the numbers/titles of the tickets that gate this one, or "None β€” can start immediately".
63
+ **Status:** ready-for-agent
64
+
65
+ - [ ] Acceptance criterion 1
66
+ - [ ] Acceptance criterion 2
67
+
68
+ ---
69
+
70
+ ## Per-ticket issue template (real tracker)
71
+
72
+ **Parent**
73
+ A reference to the parent issue on the tracker (if the source was an existing issue, otherwise omit this section).
74
+
75
+ **What to build**
76
+ The end-to-end behaviour this ticket makes work, from the user's perspective β€” not layer-by-layer implementation.
77
+
78
+ **Acceptance criteria**
79
+ - [ ] Criterion 1
80
+ - [ ] Criterion 2
81
+
82
+ **Blocked by**
83
+ A reference to each blocking ticket, or "None β€” can start immediately".
84
+
85
+ ---
86
+
87
+ In either form, avoid specific file paths or code snippets β€” they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it and note briefly that it came from a prototype. Trim to the decision-rich parts β€” not a working demo, just the important bits.
.scratch/arch-improvements/issues/01-appcontext-expand.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 01 β€” Introduce `AppContext` dataclass (Expand)
2
+
3
+ **Blocked by:** None β€” can start immediately
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Introduce a single `AppContext` dataclass that packages all runtime state currently scattered as module-level globals in `functions.py` (`_bm25_index`, `_vector_index`, `_embedding_provider`, `_hybrid_search`, `_stream_broadcaster`, and the `kv` reference). Construct one `AppContext` instance inside `create_app()` and thread it through to every function that needs it. The existing globals stay in place as a compatibility shim β€” no callers are changed in this ticket. Zero behaviour change; this is the expand step that makes the god-module split (tickets 05–08) possible.
9
+
10
+ ## Acceptance criteria
11
+
12
+ - [ ] `AppContext` dataclass exists in a new `context.py` module (or equivalent) with typed fields for `kv`, `bm25`, `vector`, `embedder`, and `broadcast`
13
+ - [ ] `create_app()` constructs one `AppContext` and stores it on the Flask app (e.g. `app.ctx`)
14
+ - [ ] The five existing `set_*` functions in `functions.py` remain working β€” globals are still set alongside the new `AppContext`
15
+ - [ ] All existing tests pass with no changes
16
+ - [ ] No route handler or worker is changed in this ticket
.scratch/arch-improvements/issues/02-privacy-module.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 02 β€” Extract `privacy.py` deep module
2
+
3
+ **Blocked by:** None β€” can start immediately
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Extract `strip_private_data()` from `functions.py` into a dedicated `privacy.py` module. The new module exposes a single `scrub(text: str, patterns=DEFAULT_PATTERNS) -> str` interface. The regex pattern list is promoted to a public, documented constant (`DEFAULT_PATTERNS`) and is extensible via an env var (`AGENTCACHE_REDACT_PATTERNS`). `functions.py` calls `privacy.scrub()` internally β€” all existing callers continue working without change. First unit tests for Privacy Scrub are added in this ticket, targeting `scrub()` directly.
9
+
10
+ ## Acceptance criteria
11
+
12
+ - [ ] `privacy.py` exists with a `scrub(text, patterns=DEFAULT_PATTERNS)` function
13
+ - [ ] `DEFAULT_PATTERNS` is a documented list of regex strings β€” visible and auditable
14
+ - [ ] An env var `AGENTCACHE_REDACT_PATTERNS` (comma-separated regex strings) appends additional patterns at startup
15
+ - [ ] `functions.py` no longer contains the scrubbing regex list β€” it imports and calls `privacy.scrub()`
16
+ - [ ] Unit tests cover: API key pattern, bearer token pattern, custom pattern via argument, no false positives on safe text
17
+ - [ ] All existing tests pass
.scratch/arch-improvements/issues/03-auth-middleware.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 03 β€” Centralise auth middleware
2
+
3
+ **Blocked by:** None β€” can start immediately
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Replace the copy-pasted `_check_auth()` function that currently lives independently in every route blueprint (`mcp.py`, `observations.py`, `memories.py`, `health.py`, and others) with a single `require_auth` decorator defined in a new `auth.py` module. Every route that previously called `_check_auth()` at the top of its handler switches to the `@require_auth` decorator. The auth logic (timing-safe `hmac.compare_digest` Bearer token check) is identical β€” this is purely a deduplication. RBAC can be added in one place in a future ticket.
9
+
10
+ ## Acceptance criteria
11
+
12
+ - [ ] `auth.py` exists with a `require_auth(f)` decorator that performs the timing-safe Bearer token check
13
+ - [ ] Every route blueprint imports and uses `@require_auth` β€” no blueprint defines its own `_check_auth`
14
+ - [ ] A request with no secret configured passes through (existing behaviour preserved)
15
+ - [ ] A request with a wrong token still gets a `401` response
16
+ - [ ] All existing tests pass
.scratch/arch-improvements/issues/04-mcp-tool-registry.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 04 β€” MCP tool registry (replace elif chain)
2
+
3
+ **Blocked by:** 03 β€” centralise auth middleware
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Replace the 300-line `if/elif` dispatch chain in `routes/mcp.py` with a `@register("tool_name")` decorator registry. Each of the 30+ MCP tool handlers becomes a standalone function decorated with `@register`. The dispatcher becomes a two-liner: look up the tool name in the registry dict and call the handler. Auth is handled by the shared `@require_auth` decorator from ticket 03. The tool schema list (`get_mcp_tools_schemas`) stays as-is. No change to the MCP wire format or agent behaviour.
9
+
10
+ ## Acceptance criteria
11
+
12
+ - [ ] A `_tools: dict[str, Callable]` registry exists and a `@register(name)` decorator populates it
13
+ - [ ] Every MCP tool handler is a standalone function β€” not an inline block inside a giant if/elif
14
+ - [ ] The POST `/mcp/tools` handler body is ≀ 20 lines (lookup + call + error handling)
15
+ - [ ] Each tool handler function is independently importable and callable in a test without starting Flask
16
+ - [ ] Adding a new tool requires only adding a new decorated function β€” no editing of existing dispatch code
17
+ - [ ] All existing MCP tool calls produce the same responses as before
.scratch/arch-improvements/issues/05-split-observations-memories.md ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 05 β€” Split god module batch 1: `observations.py` + `memories.py`
2
+
3
+ **Blocked by:** 01 β€” AppContext dataclass (Expand)
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Move the first two major domain areas out of `functions.py` into focused modules, using the domain vocabulary from `UBIQUITOUS_LANGUAGE.md`.
9
+
10
+ **`observations.py`** receives: `folder_observe()`, `observe()` (legacy), `dedup_folder_observations()`, `build_synthetic_compression()`, `infer_type()`, `extract_files()`, `extract_image()`, image store helpers (`save_image_to_disk`, `delete_image`, `touch_image`, `is_managed_image_path`), and `normalize_folder_path()` / `validate_agent_id()`.
11
+
12
+ **`memories.py`** receives: `remember()`, `forget()`, memory versioning logic, and `jaccard_similarity()`.
13
+
14
+ `functions.py` re-exports everything from these two modules so all existing callers continue to work unchanged. CI must stay green. This is the first batch of the expand phase of the god-module split.
15
+
16
+ ## Acceptance criteria
17
+
18
+ - [ ] `observations.py` and `memories.py` exist as standalone modules
19
+ - [ ] All moved functions accept `AppContext` (from ticket 01) instead of reaching into module-level globals, while also accepting the legacy `kv: StateKV` signature for backward compat
20
+ - [ ] `functions.py` re-exports every moved symbol β€” no call site outside `functions.py` needs to change
21
+ - [ ] Existing route blueprints and workers continue to import from `functions` without modification
22
+ - [ ] All existing tests pass
23
+ - [ ] No logic is changed β€” this is a pure relocation
.scratch/arch-improvements/issues/06-split-indexing-retrieval.md ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 06 β€” Split god module batch 2: `indexing.py` + `retrieval.py`
2
+
3
+ **Blocked by:** 05 β€” split batch 1 (observations + memories)
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Move the second major domain area out of `functions.py`.
9
+
10
+ **`indexing.py`** receives: `IndexPersistence`, `rebuild_index()`, `backfill_obs_lookup_if_needed()`, `vector_index_add_guarded()`, `clip_embed_input()`, and all index-related helpers. This module owns the Index Rebuild lifecycle (see `UBIQUITOUS_LANGUAGE.md`).
11
+
12
+ **`retrieval.py`** receives: `folder_search()`, `folder_timeline()`, `compile_context()`, and `export_data()`. This module owns the Recall, Smart Search, Folder Search, Timeline, and Context Compilation operations.
13
+
14
+ `functions.py` continues to re-export everything. CI stays green.
15
+
16
+ ## Acceptance criteria
17
+
18
+ - [ ] `indexing.py` and `retrieval.py` exist as standalone modules
19
+ - [ ] All moved functions use `AppContext` from ticket 01 rather than reaching into globals directly
20
+ - [ ] `functions.py` re-exports every moved symbol unchanged
21
+ - [ ] `IndexPersistence` is importable from `indexing` in `workers.py` without changing any other worker code
22
+ - [ ] `folder_search()` and `compile_context()` are callable from a test by constructing an `AppContext` with an in-memory KV and a fresh `SearchIndex` β€” no Flask app required
23
+ - [ ] All existing tests pass
24
+ - [ ] No logic is changed
.scratch/arch-improvements/issues/07-split-consolidation-slots-lessons.md ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 07 β€” Split god module batch 3: `consolidation.py` + `slots.py` + `lessons.py`
2
+
3
+ **Blocked by:** 06 β€” split batch 2 (indexing + retrieval)
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Move the final major domain areas out of `functions.py`.
9
+
10
+ **`consolidation.py`** receives: `consolidate()`, `auto_forget()`, `folder_graph_build()`, and all LLM-call helpers used exclusively by the Consolidation pipeline. This module owns the Consolidation lifecycle (Working Memory β†’ Episodic β†’ Semantic β†’ Procedural).
11
+
12
+ **`slots.py`** receives: all Slot CRUD functions (`get_slots`, `set_slot`, `delete_slot`, `reflect_slot`, `append_slot`, etc.).
13
+
14
+ **`lessons.py`** receives: all Lesson CRUD functions (`get_lessons`, `save_lesson`, `search_lessons`, `strengthen_lesson`, lesson decay logic).
15
+
16
+ `functions.py` re-exports everything. After this ticket, `functions.py` is a pure shim β€” no domain logic remains in it.
17
+
18
+ ## Acceptance criteria
19
+
20
+ - [ ] `consolidation.py`, `slots.py`, and `lessons.py` exist as standalone modules
21
+ - [ ] `functions.py` contains no domain logic β€” only re-export statements
22
+ - [ ] All moved functions use `AppContext` from ticket 01
23
+ - [ ] `auto_forget()` is callable from a test without importing `functions` directly
24
+ - [ ] All existing tests pass
25
+ - [ ] No logic is changed
.scratch/arch-improvements/issues/08-delete-functions-shim.md ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 08 β€” Contract: delete `functions.py` shim, migrate all callers to direct imports
2
+
3
+ **Blocked by:** 05, 06, 07 β€” all three split batches must be complete
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Delete `functions.py` entirely. Update every import across `routes/`, `workers.py`, `app.py`, and the test suite to point directly to the focused module that owns the symbol. This is the contract step of the expand–contract sequence. After this ticket the domain vocabulary from `UBIQUITOUS_LANGUAGE.md` is directly reflected in the module names β€” there is no god module.
9
+
10
+ Import mapping (non-exhaustive):
11
+ - `from .functions import folder_observe` β†’ `from .observations import folder_observe`
12
+ - `from .functions import remember, forget` β†’ `from .memories import remember, forget`
13
+ - `from .functions import folder_search, compile_context` β†’ `from .retrieval import folder_search, compile_context`
14
+ - `from .functions import IndexPersistence, rebuild_index` β†’ `from .indexing import IndexPersistence, rebuild_index`
15
+ - `from .functions import consolidate, auto_forget` β†’ `from .consolidation import consolidate, auto_forget`
16
+
17
+ ## Acceptance criteria
18
+
19
+ - [ ] `functions.py` does not exist in the repository
20
+ - [ ] `rg "from .functions import\|from agentcache.functions import\|import functions"` returns zero results in `src/`
21
+ - [ ] All existing tests pass with imports updated
22
+ - [ ] No test imports from `functions` β€” each test imports from the specific module it exercises
23
+ - [ ] CI is green
.scratch/arch-improvements/issues/09-typed-observations-table-expand.md ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 09 β€” Add typed `observations` table to SQLite (Expand)
2
+
3
+ **Blocked by:** 05 β€” observations module must exist to own this migration
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Add a typed `observations` table to the SQLite schema with columns for `folder`, `agent`, `timestamp`, `type`, `importance`, and `text`. When a new Observation is ingested via `folder_observe()`, write it to both the new typed table and the existing `kv_store` scope (dual-write). Reads still use `kv_store` β€” zero behaviour change for any query path. This is the expand step that makes SQL-level filtering possible without breaking anything.
9
+
10
+ The new table enables: `WHERE folder = ? AND agent = ? AND timestamp > ?` and `ORDER BY importance DESC LIMIT N` entirely at the SQLite layer β€” no Python-side filtering needed.
11
+
12
+ ## Acceptance criteria
13
+
14
+ - [ ] `observations(id TEXT PK, folder TEXT, agent TEXT, timestamp TEXT, type TEXT, importance INTEGER, text TEXT)` table exists in the DB schema with indexes on `(folder, agent, timestamp)` and `(importance)`
15
+ - [ ] `folder_observe()` dual-writes: one row to `observations`, one entry to `kv_store` (existing path)
16
+ - [ ] The DB migration runs automatically on startup if the table does not exist (no manual step)
17
+ - [ ] All reads continue to use `kv_store` β€” no query is changed in this ticket
18
+ - [ ] All existing tests pass
19
+ - [ ] A DB integrity test verifies that for each `kv_store` Observation entry written, a matching row exists in `observations`
.scratch/arch-improvements/issues/10-typed-observations-reads-contract.md ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 10 β€” Migrate Observation reads to typed table (Contract)
2
+
3
+ **Blocked by:** 09 β€” typed observations table (Expand) must be complete
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Switch `folder_search`, `folder_timeline`, `compile_context`, and `auto_forget` to read Observations from the typed `observations` table using SQL-level filtering, replacing the current pattern of `kv.list(scope)` + Python-side filtering. Stop dual-writing new Observations to `kv_store` β€” the typed table is now the single source of truth. A one-time migration backfills any existing `kv_store` Observation entries into the typed table for users upgrading from a previous version.
9
+
10
+ ## Acceptance criteria
11
+
12
+ - [ ] `folder_timeline()` issues a single `SELECT … WHERE folder=? AND agent=? ORDER BY timestamp DESC LIMIT ?` β€” no Python-side filtering loop
13
+ - [ ] `folder_search()` hydrates candidates from the typed table rather than `kv.list(scope)` for the Observation load step
14
+ - [ ] `compile_context()` fetches recent Observations using `ORDER BY importance DESC, timestamp DESC LIMIT ?` at the SQL layer
15
+ - [ ] New `folder_observe()` calls write only to the typed table (no `kv_store` Observation scope write)
16
+ - [ ] A backfill function runs once on startup to migrate existing `kv_store` Observations into the typed table
17
+ - [ ] A benchmark test (or manual note in the PR) shows query latency improvement at β‰₯ 10k Observations
18
+ - [ ] All existing tests pass
.scratch/arch-improvements/issues/11-basevectorindex-protocol.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 11 β€” `BaseVectorIndex` protocol + `InMemoryVectorIndex` adapter
2
+
3
+ **Blocked by:** 06 β€” indexing module must exist before its interface is formalised
4
+ **Status:** ready-for-agent
5
+
6
+ ## What to build
7
+
8
+ Define a `BaseVectorIndex` Protocol (or ABC) in `indexing.py` with three methods: `add(obs_id, session_id, embedding)`, `remove(obs_id)`, and `search(query_embedding, limit) -> list`. Rename the current flat-list implementation to `InMemoryVectorIndex` and have it implement `BaseVectorIndex`. Update `AppContext.vector` to be typed as `BaseVectorIndex`. Behaviour is completely unchanged β€” this ticket only cuts the adapter seam so future backends (HNSW, Qdrant, Chroma) can be swapped in without touching `retrieval.py` or `consolidation.py`.
9
+
10
+ ## Acceptance criteria
11
+
12
+ - [ ] `BaseVectorIndex` Protocol exists in `indexing.py` with `add`, `remove`, and `search` as the only required methods
13
+ - [ ] `InMemoryVectorIndex` implements `BaseVectorIndex` and passes a `isinstance(idx, BaseVectorIndex)` check
14
+ - [ ] `AppContext.vector` is typed as `BaseVectorIndex | None`
15
+ - [ ] A test constructs a minimal stub that implements `BaseVectorIndex` and passes it to `folder_search()` via `AppContext` β€” confirming the seam is real and injectable
16
+ - [ ] `VectorIndex` (old name) remains as an alias for `InMemoryVectorIndex` for one release to avoid breaking any external imports
17
+ - [ ] All existing tests pass with no behaviour change
SPIKE.md ADDED
@@ -0,0 +1,578 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Feature Spike: Small Improvements for Agent Cache User Experience
2
+
3
+ **Date:** 2026-07-16
4
+ **Priority:** Low risk, high value additions to current architecture
5
+ **Tech Stack Impact:** None β€” all fit within Flask/SQLite model
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+ 1. [Current State Analysis](#current-state-analysis)
11
+ 2. [Feature Opportunities](#feature-opportunities)
12
+ 3. [Recommendation Priority](#recommendation-priority)
13
+
14
+ ---
15
+
16
+ ## Current State Analysis
17
+
18
+ ### What agentcache-python Already Has
19
+
20
+ **Core Features Implemented:**
21
+ - REST API (Flask framework on port 3111)
22
+ - WebSocket streaming (`/stream/mem-live/viewer`)
23
+ - Hybrid search: BM25 keyword + vector embeddings (Gemini/OpenAI/SentenceTransformer)
24
+ - Folder-based memory model: `(folder_path, agent_id)` scoping
25
+ - Knowledge graph construction via `folder_graph_build()`
26
+ - MCP tools for agent integration (13+ tools)
27
+ - Security redactions in observations (`strip_private_data()`)
28
+ - Token budget limits during context compilation
29
+ - Background workers for index maintenance
30
+
31
+ **Key Functions:**
32
+ | Function | Purpose | File |
33
+ |----------|---------|------|
34
+ | `folder_observe()` | Ingest observations with scoping | `functions.py` |
35
+ | `folder_search()` | BM25 + vector hybrid search | `search.py` |
36
+ | `folder_timeline()` | Chronological activity feed | `functions.py` |
37
+ | `folder_graph_build()` | Build knowledge graph nodes/edges | `functions.py` |
38
+ | `remember()/forget()` | Global memory management | `functions.py` |
39
+ | `context()` | Compile contextual blocks with token limits | `functions.py` |
40
+ | `export_data()` | v2 JSON export format | `functions.py` |
41
+
42
+ **Key Tests:**
43
+ - `test_observe_core.py` β€” Observation validation and redaction
44
+ - `test_folder_observe.py` β€” Folder-scoped ingestion
45
+ - `test_search.py` β€” BM25/vector search logic
46
+ - `test_context.py` β€” Context compilation with token budgets
47
+ - `test_graph.py` β€” Knowledge graph construction
48
+
49
+ ---
50
+
51
+ ## Feature Opportunities
52
+
53
+ ### 🎯 Feature 1: Observation Prioritization / Weighting
54
+
55
+ **Why Add This?**
56
+ Users naturally write observations with different levels of importance. Currently all observations are weighted equally in search results. The search uses RRF (Reciprocal Rank Fusion) which already applies weights, but there's no user-controlled way to emphasize important observations.
57
+
58
+ **What to Implement:**
59
+ ```python
60
+ def folder_observe(kv, payload, priority=0): # 0-100 scale
61
+
62
+ # Store in metadata:
63
+ obs["priority"] = priority
64
+
65
+ return {
66
+ "observationId": fobs_{hash}_{rand},
67
+ "priority": priority, # Added field
68
+ ...
69
+ }
70
+ ```
71
+
72
+ **Changes Required:**
73
+ - Add `priority` field to observation payload validation (new required/optional check)
74
+ - Modify search weighting in `HybridSearch._rank()` to include observation priority:
75
+ ```python
76
+ # Current RRF: rank_weight = 1 / len(relevant_docs)
77
+ # New RRF: combined_weight = (bm25_score + vector_score) * obs_priority / 100
78
+ ```
79
+
80
+ **User Experience:**
81
+ When a user writes an observation, they can optionally mark it as high-priority:
82
+ ```python
83
+ # Tag this as critical issue - show prominently in search results
84
+ folder_observe(
85
+ kv, {
86
+ "folderPath": "/home/user/project/src/auth",
87
+ "agentId": "my-ai-agent",
88
+ "text": "JWT rotation breaks production auth endpoints",
89
+ "timestamp": now(),
90
+ "priority": 90 # Highest importance
91
+ }
92
+ )
93
+ ```
94
+
95
+ Search results for "jwt auth break" automatically rank this observation higher than lower-priority memories.
96
+
97
+ **Implementation Complexity:** **15 minutes**
98
+ - Add one field to observation validation schema
99
+ - Modify RRF weighting formula (single math operation change)
100
+ - Update tests to verify priority affects ranking
101
+
102
+ ---
103
+
104
+ ### 🎯 Feature 2: Observation Source Tracking
105
+
106
+ **Why Add This?**
107
+ Users write observations from different contexts (code files, documentation, terminal logs). Knowing which file context an observation came from helps detect when user re-wrote same code after learning a lesson, and aids in identifying relevant follow-up actions.
108
+
109
+ **What to Implement:**
110
+ ```python
111
+ def folder_observe(kv, payload, source_file=None): # e.g., "src/auth.py"
112
+
113
+ obs["sourceFile"] = source_file or None
114
+
115
+ return {
116
+ "observationId": fobs_{hash}_{rand},
117
+ "sourceFile": source_file, # New field
118
+ "text": "...",
119
+ ...
120
+ }
121
+ ```
122
+
123
+ **Changes Required:**
124
+ - Add `source_file` to observation payload validation (optional string)
125
+ - Store as additional JSON field in KV value column
126
+ - Update timeline query to optionally show source files
127
+ - Update search results to surface source file info
128
+
129
+ **User Experience:**
130
+ Search: "What did I learn about JWT?"
131
+ Results:
132
+ ```json
133
+ {
134
+ "id": "fobs_abc123",
135
+ "text": "JWT needs refresh tokens for rotation",
136
+ "sourceFile": "src/auth.py",
137
+ "timestamp": "2026-07-15T10:30:00Z"
138
+ }
139
+ ```
140
+
141
+ The source file field tracks which files were being developed when lessons were learned. Helps detect when user re-implements same pattern after reading a tutorial, or helps identify which modules need updates based on recent observations.
142
+
143
+ **Implementation Complexity:** **15 minutes**
144
+ - Add one optional field to observation schema
145
+ - Store in existing KV structure (no new table/column needed)
146
+ - Update `folder_timeline()` to surface source file info when requested
147
+
148
+ ---
149
+
150
+ ### 🎯 Feature 3: Folder-Level Statistics Endpoint
151
+
152
+ **Why Add This?**
153
+ Users need quick stats on individual folders without going to the dashboard UI. The current `/health` endpoint only returns global aggregate across all folders. Users often want: "How many observations do I have in my frontend folder?" answered via simple API call.
154
+
155
+ **What to Implement:**
156
+ ```python
157
+ # src/agentcache/routes/folder_stats.py or append to existing routes
158
+
159
+ @app.route('/folder/stats/<path:folder_path>')
160
+ def folder_stats(folder_path):
161
+ kv = StateKV() # Get current DB instance
162
+
163
+ # Count observations for this folder
164
+ obs_count = 0
165
+ try:
166
+ from agentcache.functions import KV
167
+ scope = KV.folder_obs(folder_path, os.getenv("AGENT_ID", "default"))
168
+ rows = kv.db.execute(
169
+ "SELECT COUNT(*) FROM kv_store WHERE scope = ?",
170
+ [scope]
171
+ ).fetchone()
172
+ obs_count = rows[0] if rows else 0
173
+ except:
174
+ pass
175
+
176
+ # Get last observation timestamp
177
+ last_obs_ts = None
178
+ try:
179
+ rows = kv.db.execute(
180
+ "SELECT MAX(ts) FROM kv_store WHERE scope = ?",
181
+ [scope]
182
+ ).fetchone()
183
+ last_obs_ts = rows[0] if rows else None
184
+ except:
185
+ pass
186
+
187
+ return {
188
+ "folderPath": folder_path,
189
+ "observationCount": obs_count,
190
+ "memoryCount": 123, # Get from memories scope separately
191
+ "lastObsTimestamp": last_obs_ts.isoformat() if last_obs_ts else None,
192
+ "foldersKnownToThisAgent": ["home/user/projectX", ...] # Also include this context
193
+ }
194
+ ```
195
+
196
+ **User Experience:**
197
+ REST API call returns folder stats instantly:
198
+ ```bash
199
+ curl http://localhost:3111/folder/stats/home/user/my-project
200
+ # Returns:
201
+ {
202
+ "folderPath": "home/user/my-project",
203
+ "observationCount": 42,
204
+ "memoryCount": 5,
205
+ "lastObsTimestamp": "2026-07-15T14:23:00Z",
206
+ "foldersKnownToThisAgent": ["home/user/my-project"]
207
+ }
208
+ ```
209
+
210
+ **Implementation Complexity:** **10 minutes**
211
+ - Create new endpoint following existing Flask pattern (like `/health`)
212
+ - Query existing KV methods for observation and memory counts
213
+ - Return simple JSON response
214
+
215
+ **Note:** This requires importing `StateKV` instance in new route file, which means either creating a module-scoped singleton per folder or passing the current `kv` from `app.py` via Flask's application context.
216
+
217
+ ---
218
+
219
+ ### 🎯 Feature 4: Observation Expiration / TTL (Time-To-Live)
220
+
221
+ **Why Add This?**
222
+ Temporal observations become outdated over time. Example: "Error on port 3000" observation becomes irrelevant after user changes their app to use port 8080. Currently all observations persist indefinitely. With TTL, expired observations can auto-remove from search results or be marked as "expired".
223
+
224
+ **What to Implement:**
225
+ ```python
226
+ def folder_observe(kv, payload, ttl_seconds=None):
227
+
228
+ # Store observation with expiration timestamp
229
+ obs["expiryTs"] = ts + ttl_seconds if ttl_seconds else None
230
+
231
+ return {
232
+ "observationId": fobs_{hash}_{rand},
233
+ "ttl": ttl_seconds or null, # New field
234
+ ...
235
+ }
236
+ ```
237
+
238
+ **Changes Required:**
239
+ - Add `ttl_seconds` to observation payload validation (optional)
240
+ - Store as additional timestamp field in KV value column
241
+ - Modify `folder_timeline()` to filter out expired observations by default
242
+ - Optional: Add background worker that sweeps/expunges expired observations
243
+
244
+ **User Experience:**
245
+ ```python
246
+ # Observation expires after 24 hours
247
+ folder_observe(
248
+ kv, {
249
+ "folderPath": "/home/user/app/src/server",
250
+ "agentId": "my-agent",
251
+ "text": "Default port is 3000 for development",
252
+ "timestamp": now(),
253
+ "ttl": 86400 # 24 hours
254
+ }
255
+ )
256
+
257
+ # Timeline query automatically excludes expired observations
258
+ timeline(kv, folder_path="/home/user/app/src/server")
259
+ ```
260
+
261
+ Returns timeline without "port 3000" observation after 24 hours. Users can also check for specific expiry status on an observation with additional endpoint `/folder/observation/status/<id>`.
262
+
263
+ **Implementation Complexity:** **25 minutes**
264
+ - Add TTL field to observation payload validation
265
+ - Store as extra JSON field in KV (existing table schema)
266
+ - Modify `folder_timeline()` query to include optional TTL filter:
267
+ ```python
268
+ ts > now() - ttl_seconds if ttl_seconds else None AND expiryTs > ts
269
+ ```
270
+ - Update search results to exclude expired observations
271
+ - Optional: Background worker sweeps for cleanup (simpler version doesn't need this)
272
+
273
+ **Risk:** Medium β€” requires changes to timeline query logic which users expect returns chronological activity. Must ensure backward compatibility with existing payloads that don't include TTL field (use `None` default).
274
+
275
+ ---
276
+
277
+ ### 🎯 Feature 5: Memory Version History
278
+
279
+ **Why Add This?**
280
+ Long-term memories evolve. Currently `remember()` just overwrites the old memory. Users might accidentally overwrite important information or can't undo an incorrect memory. With versioning, each remember creates a new version tracked sequentially.
281
+
282
+ **What to Implement:**
283
+ ```python
284
+ def remember(kv, data, skip_versioning=False):
285
+
286
+ # Check existing memories for this content
287
+ existing_memories = kv.get(KV.memories, "mem_abc123")
288
+
289
+ if not skip_versioning and existing_memories:
290
+ # Append version suffix to old content (preserve as text)
291
+ current_content = json.loads(existing_memories["value"])
292
+ current_content["_version"] = len(current_content.get("_versions", [])) + 1
293
+ current_content["_createdTs"] = existing_memories["ts"]
294
+
295
+ # Store in new version field
296
+ versions = current_content.get("_versions", [])
297
+ current_content["_versions"].append({
298
+ "content": current_content["content"],
299
+ "version": len(versions),
300
+ "ts": now(),
301
+ ...
302
+ })
303
+
304
+ return {
305
+ "memoryId": new_id,
306
+ "versions": [...], # New field: list of version history
307
+ ...
308
+ }
309
+ ```
310
+
311
+ **Changes Required:**
312
+ - Modify `remember()` to check for existing content match in KV memories
313
+ - Append new version as JSON object to version history array
314
+ - Store version number and timestamp for each version change
315
+ - Optionally expose version history endpoint for viewing past versions
316
+
317
+ **User Experience:**
318
+ When a user calls:
319
+ ```python
320
+ remember(
321
+ kv, {
322
+ "content": "JWT authentication uses bearer token in Authorization header",
323
+ "concepts": ["jwt", "auth"],
324
+ ...
325
+ }
326
+ )
327
+ ```
328
+
329
+ The response includes version history showing how this memory has evolved over time. Later, if the user discovers more context:
330
+ ```python
331
+ remember(
332
+ kv, {
333
+ "content": "JWT authentication uses Bearer token and JWT_SECRET key in Authorization header",
334
+ ...
335
+ }
336
+ )
337
+ ```
338
+
339
+ They can review past versions to see the evolution or optionally revert to earlier version by storing it again.
340
+
341
+ **Implementation Complexity:** **20 minutes**
342
+ - Modify `remember()` to detect duplicate content (hash match on content field)
343
+ - Store version history as JSON array in KV value column (existing table schema, just more data)
344
+ - Optionally create endpoint `/memory/version/<id>` for viewing history
345
+ - Update tests to verify version history is stored correctly
346
+
347
+ **Risk:** Low β€” simple append operation to existing data structure. No schema migration needed since we're adding fields to existing JSON values. Backward compatible since previous versions without `_versions` field just get overwritten (which may be surprising behavior, but acceptable for safety).
348
+
349
+ ---
350
+
351
+ ### 🎯 Feature 6: Search Result Citation with Source Links
352
+
353
+ **Why Add This?**
354
+ When a memory is returned from search, users can see where the original observation came from and what code context had it observed. This helps understand which files are most relevant to certain topics and provides better "view in context" capability for follow-up actions.
355
+
356
+ **What to Implement:**
357
+ ```python
358
+ def folder_observe(kv, payload):
359
+ # Store source file info
360
+ if payload.get("sourceFile"):
361
+ obs["sourceFile"] = payload["sourceFile"]
362
+ obs["lineNumber"] = payload.get("lineNumber") or None
363
+
364
+ return {
365
+ "observationId": fobs_{hash}_{rand},
366
+ "sourceFile": "src/auth.py", # New field: where this came from
367
+ ...
368
+ }
369
+ ```
370
+
371
+ **Search Result Enhancement:**
372
+ Modify search result response format:
373
+ ```python
374
+ def folder_search(kv, query, limit, folder_path, agent_id):
375
+
376
+ results = [
377
+ {
378
+ "id": obs_id,
379
+ "text": "...",
380
+ "folderPath": "/home/user/project",
381
+ "score": 0.85, # Original BM25 score or vector similarity
382
+ "sourceFile": "src/auth.py", # NEW: where this came from
383
+ "timestamp": ts.isoformat(),
384
+ ...
385
+ }
386
+ ]
387
+ ```
388
+
389
+ **User Experience:**
390
+ Search query: "JWT authentication"
391
+ Results now show source file links:
392
+ ```json
393
+ [
394
+ {
395
+ "id": "fobs_abc123",
396
+ "text": "JWT needs refresh tokens for token rotation",
397
+ "sourceFile": "src/auth.py", // Click link to see code context!
398
+ "score": 0.92,
399
+ ...
400
+ },
401
+ {
402
+ "id": "fobs_def456",
403
+ "text": "Using 'jwt' import from auth package",
404
+ "sourceFile": "src/main.py", // Another file
405
+ "score": 0.78,
406
+ ...
407
+ }
408
+ ]
409
+ ```
410
+
411
+ **Implementation Complexity:** **20 minutes**
412
+ - Add `source_file` field to observation schema (same as Feature 2)
413
+ - Update search result formatting in `folder_search()` to include source file info from observations
414
+ - Optionally: Show which files are most-relevant based on sources of highest-scoring memories
415
+
416
+ **Risk:** Low β€” just need to read the new field from observations and format it in search results. No complex data structure change needed.
417
+
418
+ ---
419
+
420
+ ### 🎯 Feature 7: Memory Impact / Related Memories Endpoint
421
+
422
+ **Why Add This?**
423
+ When a user remembers something important, they might want to know which other memories relate semantically or contextually. Currently all memories exist independently with no explicit linking mechanism. Adding semantic similarity scores helps users understand how new memories connect to existing knowledge and spot conflicts.
424
+
425
+ **What to Implement:**
426
+ ```python
427
+ def memory_related(kv, memory_id):
428
+
429
+ # Get original memory content from KV
430
+ original_memory = kv.get(KV.memories, memory_id)
431
+ original_content = json.loads(original_memory["value"])
432
+
433
+ # Search for semantically similar memories
434
+ search_results = hybrid_search.search(
435
+ query=original_content["content"],
436
+ limit=5, # Return top 5 related memories
437
+ filter_scope=[KV.memories], # Only search within memories scope
438
+ )
439
+
440
+ # Score by similarity + rank in results list
441
+ return {
442
+ "memoryId": memory_id,
443
+ "relatedMemories": [
444
+ {
445
+ "id": "mem_abc123",
446
+ "score": 0.92, # Semantic similarity score (0-1)
447
+ "reason": "Same concepts: JWT, authentication",
448
+ ...
449
+ },
450
+ ...
451
+ ]
452
+ }
453
+ ```
454
+
455
+ **User Experience:**
456
+ Request: `/agentcache/memories/related/mem_xyz123`
457
+ Response shows how this memory connects to existing knowledge:
458
+ ```json
459
+ {
460
+ "memoryId": "mem_xyz123",
461
+ "relatedMemories": [
462
+ {
463
+ "id": "mem_abc456",
464
+ "score": 0.94,
465
+ "reason": "Same concepts (JWT) and file context (src/auth.py)",
466
+ "content": "JWT uses Bearer token format in Authorization header"
467
+ },
468
+ {
469
+ "id": "mem_def789",
470
+ "score": 0.78,
471
+ "reason": "Related to auth module but different concepts",
472
+ "content": "Using Express.js for REST API development"
473
+ }
474
+ ],
475
+ ...
476
+ }
477
+ ```
478
+
479
+ **Implementation Complexity:** **30 minutes**
480
+ - Use existing `HybridSearch.search()` method with original memory content as query
481
+ - Score by rank in results (1st highest = most related) + cosine similarity score
482
+ - Format response as JSON with reason descriptions generated from concept extraction
483
+ - Create new endpoint that takes memory ID and returns related items
484
+
485
+ **Risk:** Medium β€” requires leveraging existing search infrastructure. Must ensure the semantic search is fast enough (< 50ms response time). Also need to handle empty or non-existent memories gracefully (return helpful error).
486
+
487
+ ---
488
+
489
+ ## Recommendation Priority
490
+
491
+ As a user who relies on this tool daily for code context and observation management, here's my prioritization based on "bang for buck" β€” quick implementation with high utility:
492
+
493
+ ### **Priority 1 (Implement First): Observation Weighting**
494
+
495
+ **Why #1:** Fastest to implement (~10 min), highest immediate value
496
+ - Let me tag observations as priority 90-100 and have them surface prominently in search results
497
+ - No new data structures needed; just add one number to the existing weighting formula
498
+ - Users already write some observations as "important issues" but they're weighted same as trivial notes
499
+
500
+ **Implementation:**
501
+ ```python
502
+ # New payload validation
503
+ def folder_observe(kv, payload, priority=0):
504
+ # Validate priority is 0-100 range
505
+ if not (0 <= priority <= 100):
506
+ raise ValueError("priority must be between 0 and 100")
507
+ ```
508
+
509
+ **Time:** ~10 min
510
+ **Risk:** None β€” modifies existing RRF function slightly
511
+
512
+ ---
513
+
514
+ ### **Priority 2: Folder Statistics Endpoint**
515
+
516
+ **Why #2:** Simplest implementation with high utility for daily users
517
+ - Users constantly need this info but it's not easily available via API calls
518
+ - Just needs a few more lines of code following Flask pattern
519
+ - Query existing data without any schema changes
520
+
521
+ **Implementation:**
522
+ ```python
523
+ @app.route('/folder/stats/<path:folder_path>')
524
+ def folder_stats():
525
+ # ~5 min implementation
526
+ ```
527
+
528
+ **Time:** ~5 min
529
+ **Risk:** None β€” new endpoint, minimal queries
530
+
531
+ ---
532
+
533
+ ### **Priority 3: Observation Source Tracking**
534
+
535
+ **Why #3:** Low risk, very useful for detecting patterns
536
+ - Track which files user has observed about certain topics helps detect learning cycles
537
+ - One extra field in KV value dict β€” no schema changes
538
+ - Helps spot when user re-implements same code after reading tutorial
539
+
540
+ **Implementation:**
541
+ ```python
542
+ def folder_observe(kv, payload, source_file=None):
543
+ # ~10 min implementation
544
+ ```
545
+
546
+ **Time:** ~15 min
547
+ **Risk:** Low β€” one optional field addition
548
+
549
+ ---
550
+
551
+ ### **Priority 4: Memory Version History**
552
+
553
+ **Why #4:** Good safety feature, relatively simple to implement
554
+ - Prevents accidental data loss
555
+ - Version history array in JSON value column is trivial to add
556
+ - Users love being able to see evolution of memories they've written
557
+
558
+ **Implementation:**
559
+ ```python
560
+ def remember(kv, data):
561
+ # ~15 min implementation
562
+ ```
563
+
564
+ **Time:** ~20 min
565
+ **Risk:** Low β€” append operation, no schema changes
566
+
567
+ ---
568
+
569
+ ## Summary
570
+
571
+ | Feature | Est. Time | Risk Level | User Impact |
572
+ |---------|-----------|------------|-------------|
573
+ | Observation Weighting | 10 min | None | High (control over search ranking) |
574
+ | Folder Stats Endpoint | 5 min | None | High (quick stats access) |
575
+ | Source Tracking | 15 min | Low | Medium (detect patterns) |
576
+ | Memory Versioning | 20 min | Low | Medium (safety feature) |
577
+
578
+ All four fit seamlessly into existing Flask/SQLite architecture without requiring any architectural changes or new dependencies. They leverage current infrastructure and enhance the user experience incrementally.
UBIQUITOUS_LANGUAGE.md ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ubiquitous Language β€” agentcache
2
+
3
+ > This glossary defines the canonical terms used in agentcache's domain model.
4
+ > Use these exact names in code, docs, PR descriptions, ticket titles, and conversations.
5
+ > "Aliases to avoid" lists terms found in the codebase or tickets that should be replaced.
6
+ > Last updated: July 2026 β€” incorporates architecture review, ubiquitous language session, and ticket breakdown.
7
+ >
8
+ > **Architecture improvement tickets:** `.scratch/arch-improvements/issues/`
9
+
10
+ ---
11
+
12
+ ## Core Memory Model
13
+
14
+ | Term | Definition | Aliases to avoid |
15
+ |------|------------|-----------------|
16
+ | **Observation** | A single captured event from an agent's execution β€” one tool call, one prompt, one file edit β€” scoped to a Folder Pair. The atomic unit of Working Memory. | `log`, `event`, `entry`, `raw_observation`, `hook event` |
17
+ | **Memory** | A manually saved, long-term insight, decision, or pattern β€” not derived from a single tool call but from deliberate agent reflection. | `long-term memory`, `global memory`, `insight`, `fact` (when used alone) |
18
+ | **Lesson** | A reusable learning with a confidence score that decays over time; strengthened when the same pattern recurs. | `learning`, `tip`, `rule` |
19
+ | **Slot** | A named, pinned piece of context β€” manually set or auto-populated β€” injected into every session start. | `pinned slot`, `memory slot`, `context slot` |
20
+ | **Folder Pair** | The primary scope key: the combination of a `folderPath` and an `agentId` that uniquely identifies one agent's memory for one project directory. | `(folder, agent)`, `folder-agent pair`, `scope key`, `session` (when used to mean this) |
21
+ | **Folder Metadata** | Aggregate statistics for a Folder Pair: observation count, last updated timestamp, and optional summary. | `folder meta`, `FolderMeta` |
22
+
23
+ ---
24
+
25
+ ## Memory Tiers (Consolidation Pipeline)
26
+
27
+ | Term | Definition | Aliases to avoid |
28
+ |------|------------|-----------------|
29
+ | **Working Memory** | The live, unprocessed stream of Observations for a Folder Pair β€” raw and ephemeral. | `raw observations`, `session logs` |
30
+ | **Episodic Memory** | A compressed summary of a group of Observations for a Folder Pair, produced by the Consolidation pipeline. | `session summary`, `compressed episode`, `narrative` |
31
+ | **Semantic Memory** | Stable factual knowledge extracted from multiple Episodic Memories by the Consolidation pipeline. | `fact`, `knowledge`, `semantic fact` |
32
+ | **Procedural Memory** | A reusable workflow or decision pattern extracted from repeated cross-session patterns. | `procedure`, `workflow memory`, `pattern` (when used alone) |
33
+ | **Consolidation** | The LLM-driven pipeline that compresses Working Memory β†’ Episodic β†’ Semantic β†’ Procedural. Triggered manually or by the auto-forget sweep. | `crystallize`, `compress`, `summarize` |
34
+
35
+ ---
36
+
37
+ ## Agents & Identity
38
+
39
+ | Term | Definition | Aliases to avoid |
40
+ |------|------------|-----------------|
41
+ | **Agent** | An AI coding assistant (Claude, Kiro, Cursor, Cline, etc.) that writes Observations and reads Memory. | `client`, `AI`, `bot` |
42
+ | **Agent ID** | A string identity for one agent instance, used as the second key in a Folder Pair. | `agentId`, `agent_id`, `agent name` |
43
+ | **Agent Scope** | The isolation boundary for an Agent's data β€” either `shared` (sees all agents' data) or `isolated` (sees only its own). | `scope isolation`, `agent namespace` |
44
+ | **MCP Client** | Any tool (Cursor, Claude Desktop, Kiro) that communicates with agentcache using the Model Context Protocol. | `plugin`, `integration`, `agent client` |
45
+
46
+ ---
47
+
48
+ ## Search & Retrieval
49
+
50
+ | Term | Definition | Aliases to avoid |
51
+ |------|------------|-----------------|
52
+ | **Recall** | A keyword-based search over Observations and Memories using the BM25 index. | `search`, `lookup`, `query` (when BM25 is implied) |
53
+ | **Smart Search** | A hybrid search combining BM25 keyword matching and vector cosine similarity, fused by Reciprocal Rank Fusion (RRF). | `semantic search`, `hybrid search`, `vector search` |
54
+ | **Folder Search** | A Recall or Smart Search scoped to one or more Folder Pairs. | `scoped search`, `project search` |
55
+ | **Timeline** | A chronological, filterable feed of Observations for a Folder Pair. | `activity feed`, `history`, `replay feed` |
56
+ | **Context Compilation** | The process of assembling Slots, Lessons, recent Episodic Memories, and a Profile into a single text block for agent injection at session start. | `context injection`, `context window prep`, `compile_context` |
57
+
58
+ ---
59
+
60
+ ## Lifecycle & Maintenance
61
+
62
+ | Term | Definition | Aliases to avoid |
63
+ |------|------------|-----------------|
64
+ | **Auto-Forget** | A background sweep that evicts Observations past their TTL or above the per-Folder-Pair cap, optionally running Consolidation before eviction. | `auto_forget`, `TTL sweep`, `pruning`, `cleanup` |
65
+ | **Deduplication** | Removal of Observations whose text fingerprint (SHA-256) matches an existing Observation in the same Folder Pair. | `dedup`, `duplicate removal` |
66
+ | **Privacy Scrub** | The process of stripping secrets, tokens, and API keys from Observation text before storage, using a regex-based policy. | `redaction`, `sanitization`, `strip_private_data` |
67
+ | **Index Rebuild** | A background process that reprocesses all stored Observations and Memories into a fresh BM25 and vector index. | `reindex`, `rebuild_index`, `warm-up` |
68
+ | **Audit Entry** | An append-only record of every write operation, stored in the audit log with agent ID and timestamp. | `audit log`, `commit`, `version` (legacy Dolt term β€” avoid) |
69
+
70
+ ---
71
+
72
+ ## Knowledge Graph
73
+
74
+ | Term | Definition | Aliases to avoid |
75
+ |------|------------|-----------------|
76
+ | **Relation** | A directed edge in the Knowledge Graph between two concepts, files, or Folder Pairs. | `graph edge`, `link`, `connection` |
77
+ | **Knowledge Graph** | The set of Relations extracted from Observations, used to augment search retrieval with structural codebase topology. | `graph`, `concept graph`, `project graph` |
78
+ | **Graph Extraction** | The LLM-driven process that reads Observations and produces Relations for the Knowledge Graph. | `graph build`, `relation extraction` |
79
+
80
+ ---
81
+
82
+ ## Architecture & Module Structure
83
+
84
+ These terms describe the codebase's structural concepts, used in tickets, PRs, and architecture discussions.
85
+
86
+ | Term | Definition | Aliases to avoid |
87
+ |------|------------|-----------------|
88
+ | **AppContext** | A single injectable dataclass that packages all runtime state (`kv`, `bm25`, `vector`, `embedder`, `broadcast`) β€” the replacement for module-level globals. Constructed once in `create_app()` and passed explicitly to every function that needs it. | `globals`, `module state`, `app state` |
89
+ | **Deep Module** | A module with a narrow public interface that hides substantial implementation β€” the opposite of a shallow wrapper. The goal of every split in the god-module refactor. | `service`, `component`, `helper` |
90
+ | **Adapter Seam** | The interface point where one concrete implementation can be swapped for another (e.g. `BaseVectorIndex` allows `InMemoryVectorIndex` to be replaced by an HNSW adapter). A seam becomes real once two concrete adapters exist. | `interface`, `abstraction layer`, `plugin point` |
91
+ | **Expand–Contract** | The two-phase refactor pattern used for wide changes: first *expand* (add the new form alongside the old so CI stays green), then *contract* (delete the old form once all callers are migrated). | `big bang refactor`, `flag day`, `rewrite` |
92
+ | **God Module** | An anti-pattern: a single module (`functions.py` at 4 583 lines) containing all domain logic, making it impossible to test any concern in isolation. Being eliminated via tickets 01–08. | `utils.py`, `helpers.py`, `core.py` (when used as a catch-all) |
93
+ | **Auth Middleware** | A single `require_auth` decorator in `auth.py` that performs the timing-safe Bearer token check β€” the replacement for copy-pasted `_check_auth()` functions in every blueprint. | `_check_auth`, `auth function`, `bearer check` |
94
+ | **MCP Tool Registry** | A `dict[str, Callable]` populated by `@register("tool_name")` decorators, replacing the 300-line `if/elif` dispatch chain in `routes/mcp.py`. | `tool dispatcher`, `elif chain`, `tool handler map` |
95
+ | **Privacy Policy** | The documented, extensible set of regex patterns that defines what text is scrubbed before an Observation is stored. Lives in `privacy.py` as `DEFAULT_PATTERNS`. | `regex list`, `scrub rules`, `redaction rules` |
96
+ | **Tracer Bullet** | A narrow, complete vertical slice of work that cuts through every layer (storage, logic, API, tests) and is demoable on its own. The unit of work in the ticket breakdown. | `task`, `story`, `sub-task` |
97
+
98
+ ---
99
+
100
+ ## Infrastructure & Sync
101
+
102
+ | Term | Definition | Aliases to avoid |
103
+ |------|------------|-----------------|
104
+ | **Folder Path** | The absolute filesystem path of the working directory that scopes a Folder Pair. | `cwd`, `directory`, `project path` |
105
+ | **KV Store** | The SQLite-backed key-value store that persists all domain data as JSON values keyed by `(scope, key)`. | `database`, `store`, `StateKV` |
106
+ | **HF Sync** | The process of backing up the KV Store to a private HuggingFace dataset repo and restoring it on startup. | `sync`, `backup`, `dataset sync` |
107
+ | **Audit High-Water Mark** | The `MAX(id)` of the audit log, used by HF Sync to detect whether any writes have occurred since the last backup. | `HWM`, `sync cursor`, `change marker` |
108
+ | **Index Shard** | A chunk of the serialized BM25 or vector index stored in the KV Store for persistence across restarts. | `index chunk`, `bm25 shard`, `vector shard` |
109
+
110
+ ---
111
+
112
+ ## Relationships
113
+
114
+ - An **Observation** belongs to exactly one **Folder Pair**.
115
+ - A **Folder Pair** has exactly one **Folder Path** and exactly one **Agent ID**.
116
+ - A **Folder Pair** accumulates many **Observations** and zero or one **Folder Metadata**.
117
+ - **Consolidation** consumes **Observations** from a **Folder Pair** and produces **Episodic Memories**, which in turn feed **Semantic Memory** and **Procedural Memory**.
118
+ - A **Lesson** exists globally β€” not scoped to a **Folder Pair**.
119
+ - A **Slot** is scoped to either a project (matched by **Folder Path**) or global (all agents).
120
+ - A **Memory** is global β€” not scoped to a **Folder Pair** β€” and may link to source **Observations** via `sourceObservationIds`.
121
+ - **Recall** and **Smart Search** operate across all **Folder Pairs** (optionally filtered) and also include **Memories**.
122
+ - **Context Compilation** assembles **Slots** + **Lessons** + **Episodic Memories** + **Profile** for one **Folder Pair** at session start.
123
+ - An **AppContext** is constructed once per process and holds one **KV Store**, one BM25 index, one **Adapter Seam** for the vector index, one embedder, and one broadcast callable.
124
+ - The **Auth Middleware** guards every route β€” the **MCP Tool Registry** handles dispatch after auth passes.
125
+ - The **Privacy Policy** is applied to every **Observation** before it is written to the **KV Store** or the typed observations table.
126
+
127
+ ---
128
+
129
+ ## Flagged Ambiguities
130
+
131
+ - **"session"** is used in two conflicting ways:
132
+ 1. *Legacy* β€” a `Session` object with a `sessionId`, from the pre-folder model (now read-only, used only for migration).
133
+ 2. *Informal* β€” an agent's working period within a Folder Pair.
134
+ β†’ Use **Folder Pair** for the scope concept and avoid "session" entirely in new code.
135
+
136
+ - **"memory"** is used both as the general concept ("agent memory system") and as the specific entity (`Memory` β€” a saved insight).
137
+ β†’ In domain conversations, always qualify: **Working Memory**, **Episodic Memory**, **Semantic Memory**, **Procedural Memory**, or the entity **Memory** (capitalised).
138
+
139
+ - **"crystallize" vs "consolidate"** β€” the codebase uses both for the same pipeline (LLM compression of Observations into higher-tier memories).
140
+ β†’ Canonical term: **Consolidation**. `crystallize` is an alias to retire.
141
+
142
+ - **"recall" vs "search" vs "smart search"** β€” three terms for overlapping operations.
143
+ β†’ **Recall** = BM25 keyword search. **Smart Search** = hybrid BM25 + vector. **Folder Search** = either, scoped to a Folder Pair.
144
+
145
+ - **"forget" vs "auto-forget" vs "evict"** β€” used interchangeably in code and docs.
146
+ β†’ **Forget** = deliberate deletion triggered by an agent. **Auto-Forget** = background TTL sweep. **Eviction** = the act of removing one Observation during Auto-Forget.
147
+
148
+ ---
149
+
150
+ ## Example Dialogue
151
+
152
+ > **Dev:** "When an agent finishes a task, should it call `remember` or does the system handle that automatically?"
153
+
154
+ > **Domain expert:** "Those are two different things. `remember` is for **Memories** β€” things the agent explicitly wants to preserve, like an architecture decision. The system automatically stores every tool call as an **Observation** in the **Folder Pair** via `folder_observe`. **Memories** are always deliberate."
155
+
156
+ > **Dev:** "So if the agent never calls `remember`, does it lose everything?"
157
+
158
+ > **Domain expert:** "No β€” the **Observations** are still there in **Working Memory** for that **Folder Pair**. But they're raw and noisy. **Consolidation** is what turns them into clean **Episodic Memories** that survive **Auto-Forget** eviction."
159
+
160
+ > **Dev:** "What's the difference between a **Lesson** and a **Semantic Memory**?"
161
+
162
+ > **Domain expert:** "A **Lesson** is something the agent explicitly learned and wants to reuse β€” it has a confidence score that decays. **Semantic Memory** is extracted automatically by the **Consolidation** pipeline from patterns across **Episodic Memories**. You save a **Lesson**; the system extracts **Semantic Memory**."
163
+
164
+ > **Dev:** "And a **Slot** is different from both?"
165
+
166
+ > **Domain expert:** "Yes. A **Slot** is pinned context β€” the agent or developer sets it manually and it gets injected at every session start via **Context Compilation**. Think of it as a sticky note, not a memory."
Users/ykanagaraj/Downloads/agentcache/CLAUDE.md ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with this repository.
4
+
5
+ ## πŸš€ Development Commands
6
+
7
+ ### Installation
8
+ To install all required dependencies, run:
9
+ ```bash
10
+ pip install -r requirements.txt
11
+ ```
12
+
13
+ ### Running the Application
14
+ To start the Flask API server, run:
15
+ ```bash
16
+ python src/app.py
17
+ ```
18
+ The server defaults to listening on port `3111`.
19
+
20
+ ### Testing
21
+ To run unit and integration tests (using pytest):
22
+ ```bash
23
+ pip install -e ".[dev]"
24
+ pytest tests/ -v
25
+ ```
26
+
27
+ ## πŸ›οΈ High-Level Architecture
28
+
29
+ The application follows a modular, layered architecture designed around Flask and dynamic data services:
30
+
31
+ 1. **Presentation Layer (Flask App)**: The core is orchestrated by `src/app.py`, which initializes the Flask application. It handles routing via registered blueprints, manages request processing, and sets up network services like WebSockets.
32
+ 2. **Data & Persistence Layer (`src/db.py`)**: Data management is handled by `StateKV`, which wraps SQLite to provide namespaced storage for observations, memories, and metadata. This layer ensures data persistence across sessions. Key scopes include `mem:folders` (index), `mem:folder:{path}:{agent}` (observations), `mem:foldermeta:{path}:{agent}` (metadata), `mem:obs_lookup` (O(1) reverse lookup), `mem:memories` (global), and `mem:index:bm25:*` (search).
33
+ 3. **Core Logic & Services (`src/functions.py`)**: This module contains all business logic implementations, including observation ingestion (`folder_observe`), timeline generation (`folder_timeline`), memory management functions (`remember`, `forget`), and the abstraction layers for embedding providers and search index operations.
34
+ 4. **Search Indexing (`src/search.py`)**: This layer manages two primary search indexes: a **BM25** index (for keyword matching) and a **Vector Index** (for semantic similarity). It dynamically selects an embedding provider (Gemini, OpenAI, or SentenceTransformer) to power the Vector Index, allowing for hybrid (RRF fusion) searches.
35
+ 5. **Communication Layer (WebSockets)**: The system supports real-time communication via WebSockets (`flask_sock`). This layer is responsible for broadcasting live events across connected clients, managed by functions in `src/functions.py`.
36
+
37
+ **Key Architectural Insight:** The architecture relies heavily on dynamic dependency injection. The application dynamically selects the best Embedding Provider based on environment variables (Gemini > OpenAI > Local Model), ensuring flexibility in how semantic search is performed, all orchestrated from the central `create_app()` function.
38
+
39
+ ## MCP Tools (12 active)
40
+
41
+ The system exposes 12 Memory/Tool endpoints via `/mcp/tools`. These tools provide agents with access to core functionalities like observation ingestion (`agent_observe`), memory recall (`memory_recall`), and persistence operations (`memory_save`, `memory_forget`). Their schemas can be found at `GET /agentcache/mcp/tools`.
42
+
43
+ ## Environment & Configuration Notes
44
+
45
+ * **Persistence**: SQLite file lives at `~/.agentcache/agentcache.db`.
46
+ * **Configuration**: Application settings are loaded from environment variables, often managed via a `.env` file in `~/.agentcache/`. Credentials (like API keys) must be set for secured operations. Key variables include `GEMINI_API_KEY`, `OPENAI_API_KEY`, `AGENTCACHE_SECRET`, and scope filters like `AGENTCACHE_AGENT_SCOPE=isolated`.
47
+ * **Data Limits**: Observe ingestion is capped at `MAX_OBS_PER_FOLDER` (default 2000). Context compilation is capped by `TOKEN_BUDGET` (default 2000).
48
+
49
+ ## Memory Structure Overview
50
+
51
+ The system supports folder-based observation scoping, global memories (`remember`/`forget`), and timeline tracking. All persistent data interaction flows through the `src/db.py` StateKV layer to maintain namespaced and auditable storage.
52
+
53
+ - [CLAUDE.md](file.md) β€” hook
product_roadmap.md ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Project Roadmap: Prototype to Production
2
+
3
+ This document outlines the phased roadmap for scaling our Memory Server project from a prototype to a robust, production-ready product. Each phase builds upon the last, focusing sequentially on Security, Features, Deployment, and Monitoring.
4
+
5
+ ## Phase 1: Stabilization & Hardening (Security First)
6
+ **Goal:** Eliminate critical vulnerabilities, patch security gaps, and ensure core functionality is robust against common exploits.
7
+
8
+ ### Tasks
9
+ 1. **Security Audit**: Review all authentication logic (`AGENTCACHE_SECRET` usage) across API endpoints to enforce strict access control checks.
10
+ 2. **Input Sanitization**: Implement rigorous input sanitization for all data ingress points (API calls, WebSocket messages) to mitigate SQL injection and XSS risks.
11
+ 3. **Dependency Hardening**: Audit `requirements.txt` and update all dependencies to their latest secure versions, flagging any version downgrade/upgrade that could introduce a vulnerability.
12
+ 4. **Test Baseline**: Run full test suite (`pytest`) and fix every failing test before moving to Phase 2.
13
+
14
+ ## Phase 2: Feature Parity & Depth
15
+ **Goal:** Implement all planned features that were marked as incomplete or missing, bringing the system closer to its intended scope.
16
+
17
+ ### Tasks
18
+ 1. **Memory Deep Dive**: Fully implement complex memory routines like `folder_graph_build` and ensure atomic behavior for `remember`/`forget` operations.
19
+ 2. **Search Optimization**: Optimize the hybrid search component (RRF parameters) and profile database queries to improve retrieval performance.
20
+ 3. **MCP Tool Completion**: Complete functionality for all remaining tools listed in `/mcp/tools`.
21
+ 4. **UX Polish**: Enhance the dashboard viewer (`/viewer`) to provide clear, actionable feedback on system status (e.g., current embedding model, load metrics).
22
+
23
+ ## Phase 3: Productionization & Deployment
24
+ **Goal:** Prepare the application for a stable, scalable production environment.
25
+
26
+ ### Tasks
27
+ 1. **Containerization**: Write optimized Dockerfiles for the Flask application and dependencies to ensure environment parity across environments.
28
+ 2. **Configuration Decoupling**: Refactor configuration loading to strictly adhere to container-native practices (using ENV variables exclusively) rather than relying on local `.env` file structures for production use.
29
+ 3. **Performance Tuning**: Conduct load testing to identify I/O bottlenecks (DB access, network latency). Tune database indices and optimize WebSocket message throughput.
30
+ 4. **Scalability Design**: Analyze the statelessness of components to determine optimal strategies for horizontal scaling (e.g., separate API workers vs. WebSocket workers).
31
+
32
+ ## Phase 4: Monitoring & Iteration (Live Operations)
33
+ **Goal:** Establish continuous monitoring and a feedback loop for post-launch maintenance.
34
+
35
+ ### Tasks
36
+ 1. **Observability Stack**: Implement structured logging across all modules to facilitate easy debugging in production.
37
+ 2. **Metrics Collection**: Integrate Prometheus metrics collection for tracking latency, resource utilization, and error rates.
38
+ 3. **Anomaly Detection**: Configure alerts for performance degradation or excessive error rates in the data access layer.
39
+ 4. **Feedback Mechanism**: Design and implement a simple feedback mechanism for collecting post-launch bug reports and feature requests.
questions.md ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Architectural Questions for agentcache
2
+
3
+ These are design and architecture questions raised by the current codebase.
4
+ They are not criticisms β€” they are things worth understanding and deciding on
5
+ before the system grows. Each one points to a real trade-off or risk.
6
+
7
+ ---
8
+
9
+ ## 1. Flat KV Schema
10
+
11
+ The entire data model lives in one SQLite table: `kv_store(scope, key, value TEXT)`.
12
+ Observations, memories, index shards, sessions, lessons, slots, graph edges, and
13
+ image refs all share this single namespace.
14
+
15
+ - What happens to read performance when this table grows to 100k+ rows?
16
+ - Every "query" loads an entire scope into Python and filters in memory β€” no
17
+ SQL-level filtering by timestamp, type, or importance. Is that intentional?
18
+ - Why not give each data type its own table with typed columns, which would
19
+ enable proper indexes and range queries at the DB level?
20
+
21
+ ---
22
+
23
+ ## 2. In-Memory Index with a Cold-Start Window
24
+
25
+ The BM25 and vector indexes live entirely in Python process memory. On restart,
26
+ the server either deserializes them from SQLite or rebuilds them from scratch in
27
+ a background thread.
28
+
29
+ - During the rebuild window, searches return results from an empty or stale
30
+ index. Users get silently degraded results with no indication.
31
+ - Is there a "index warming" status exposed anywhere? The `/health` endpoint
32
+ could surface this, but does it?
33
+ - For large datasets, how long does the rebuild take? Is there a bound on that?
34
+
35
+ ---
36
+
37
+ ## 3. Vector Index is a Flat Python List
38
+
39
+ `VectorIndex.search()` iterates over every stored vector and computes cosine
40
+ similarity one by one. This is O(n) per query.
41
+
42
+ - At 10k observations this is already slow. At 100k it is unusable.
43
+ - There is no ANN (approximate nearest neighbor) index β€” no HNSW, no IVF, no
44
+ external vector DB.
45
+ - The EPIC mentions Qdrant/Chroma as future backends. What is the plan and
46
+ threshold for switching?
47
+
48
+ ---
49
+
50
+ ## 4. Single Auth Token (No RBAC)
51
+
52
+ Authentication is one shared secret compared with `hmac.compare_digest`. Every
53
+ client β€” viewer, agent, admin β€” uses the same token.
54
+
55
+ - If the token leaks from one agent config, everything is exposed.
56
+ - The viewer dashboard, the `/forget` endpoint (which can delete all data), and
57
+ the `/migrate` endpoint all share the same credential level.
58
+ - The EPIC mentions RBAC (viewer/agent/admin tokens). Is there a plan to
59
+ implement this before exposing the server to a network?
60
+
61
+ ---
62
+
63
+ ## 5. Global Module-Level State
64
+
65
+ `functions.py` declares `_bm25_index`, `_vector_index`, `_embedding_provider`,
66
+ `_hybrid_search`, and `_stream_broadcaster` as module-level globals. They are
67
+ set once at startup and mutated by every request.
68
+
69
+ - This makes the application impossible to unit test in isolation (you cannot
70
+ inject a fresh index per test).
71
+ - It also makes it impossible to run two instances in the same process (e.g.
72
+ for testing).
73
+ - Was this a deliberate simplicity trade-off, or just how it grew?
74
+
75
+ ---
76
+
77
+ ## 6. No Rate Limiting or Input Size Caps on Most Endpoints
78
+
79
+ Observations are capped at 4000 characters. But memories, lessons, slots, and
80
+ graph edges have no enforced size limits. The `/agent/observe` endpoint accepts
81
+ arbitrary JSON payloads.
82
+
83
+ - A misbehaving or compromised agent could flood the server with gigabytes of
84
+ data and exhaust disk space.
85
+ - There is no per-agent write rate limit, no total storage quota, and no
86
+ backpressure mechanism.
87
+ - Is this acceptable for the current single-user local use case? What changes
88
+ if this is exposed as a shared service?
89
+
90
+ ---
91
+
92
+ ## 7. HuggingFace Sync Backs Up the Entire SQLite File
93
+
94
+ `sync.py` uploads the whole `agentcache.db` file to a HuggingFace dataset repo,
95
+ including the BM25/vector index shards stored inside it.
96
+
97
+ - The index shards can be large and are fully reproducible from raw observations.
98
+ Syncing them wastes bandwidth and storage.
99
+ - The backup is not incremental β€” it uploads the whole file every time the
100
+ audit high-water mark changes.
101
+ - If the HF token is compromised, all user memory data (observations, memories,
102
+ lessons) is exposed. Is that risk documented anywhere for users?
103
+
104
+ ---
105
+
106
+ ## 8. `functions.py` is ~2000 Lines β€” a God Module
107
+
108
+ All business logic lives in one file: observation ingestion, memory CRUD,
109
+ search orchestration, consolidation, auto-forget, graph building, slot
110
+ management, lessons, image store, index persistence, and privacy scrubbing.
111
+
112
+ - This makes it hard to test any single concern in isolation.
113
+ - It creates implicit coupling β€” a change to how observations are stored can
114
+ accidentally affect how the index is rebuilt or how memories are versioned.
115
+ - What would it look like to split this into focused modules
116
+ (e.g. `observations.py`, `memories.py`, `indexing.py`, `consolidation.py`)?
117
+
118
+ ---
119
+
120
+ ## 9. Jaccard Similarity for Memory Deduplication
121
+
122
+ `remember()` checks for duplicate memories by computing Jaccard similarity
123
+ (word overlap) between the new content and all existing memories, with a
124
+ threshold of 0.7.
125
+
126
+ - This loads all memories into Python memory on every `remember()` call.
127
+ - Jaccard on raw word sets is a weak signal for semantic similarity β€” two
128
+ memories about the same concept but phrased differently score low and
129
+ both get stored.
130
+ - With a vector embedding provider active, why not use cosine similarity
131
+ against the vector index for deduplication instead?
132
+
133
+ ---
134
+
135
+ ## 10. The MCP Endpoint Duplicates Auth and Dispatch Logic
136
+
137
+ `routes/mcp.py` has its own `_check_auth()` function, its own `_get_kv()` helper,
138
+ and a large `if/elif` chain dispatching 30+ tool names. The same auth logic
139
+ exists in other route blueprints too.
140
+
141
+ - Auth is copy-pasted across at least 3 places. A future change (e.g. adding
142
+ RBAC) would require updating each copy.
143
+ - The tool dispatch chain has no registry β€” adding a new tool requires editing
144
+ a long elif block rather than registering a handler.
145
+ - Was there a reason not to use a decorator-based dispatch registry or a shared
146
+ auth middleware?
147
+
148
+ ---
149
+
150
+ ## 11. Privacy Scrubbing is a Regex List β€” Not a Policy
151
+
152
+ `strip_private_data()` removes secrets by matching a hardcoded list of regex
153
+ patterns (API keys, tokens, passwords). It runs on raw observation text before
154
+ storage.
155
+
156
+ - If an agent logs a secret with an unusual key name (e.g. `my_service_cred`),
157
+ it passes through unredacted.
158
+ - The scrubbing happens *after* the data is already in the Python process β€”
159
+ if the process crashes mid-scrub, the raw data may already be partially
160
+ visible in a WAL frame.
161
+ - Is the current regex set documented so users can audit and extend it?
162
+
163
+ ---
164
+
165
+ ## 12. No Tests
166
+
167
+ The `tests/` directory appears to be empty or minimal. For a system that
168
+ persists agent memory and is used as a source of truth for AI context:
169
+
170
+ - How do you verify that `folder_search` returns the right results after an
171
+ observation is added and the index is updated?
172
+ - How do you catch regressions in privacy scrubbing when you add a new pattern?
173
+ - How do you verify that the HF sync does not upload stale data after a
174
+ failed WAL checkpoint?
175
+ - What is the plan for adding test coverage before the system is used in
176
+ production or shared with other developers?