From 1e3996db4b6defe806aeefd52859169d02ac335c Mon Sep 17 00:00:00 2001
From: Lawliet2004 <168631200+Lawliet2004@users.noreply.github.com>
Date: Fri, 6 Nov 2026 20:01:46 +0530
Subject: [PATCH 1/3] fix(s3vectors): query directly and follow continuation
tokens
---
src/dynavec/stores/async_s3vectors.py | 35 ++++------
src/dynavec/stores/s3vectors.py | 37 ++++-------
tests/test_async_s3vectors.py | 94 +++++++++++++++++++++------
tests/test_logging.py | 7 +-
tests/test_s3vectors.py | 91 +++++++++++++++++++++-----
tests/test_s3vectors_errors.py | 76 ++++++++++------------
6 files changed, 211 insertions(+), 129 deletions(-)
diff --git a/src/dynavec/stores/async_s3vectors.py b/src/dynavec/stores/async_s3vectors.py
index f3fd66e..db3fc62 100644
--- a/src/dynavec/stores/async_s3vectors.py
+++ b/src/dynavec/stores/async_s3vectors.py
@@ -257,7 +257,7 @@ async def query_pages(
return_distance: bool = True,
page_size: int | None = None,
) -> AsyncIterator[list[dict[str, Any]]]:
- """Yield result pages from the async S3 Vectors paginator."""
+ """Yield async result pages, following S3 Vectors ``nextToken``."""
if top_k <= 0:
return
@@ -289,22 +289,13 @@ async def query_pages(
yielded = 0
buffer: list[dict[str, Any]] = []
- with translate_client_errors():
- paginator = client.get_paginator("query_vectors")
- page_iterator = paginator.paginate(
- PaginationConfig={"MaxItems": top_k},
- **kwargs,
- ).__aiter__()
-
while True:
if self._query_limiter is not None:
await self._query_limiter.acquire_async()
- try:
- with translate_client_errors():
- page = await page_iterator.__anext__()
- except StopAsyncIteration:
- break
+ # Older supported SDKs expose QueryVectors without a paginator.
+ with translate_client_errors():
+ page = await client.query_vectors(**kwargs)
vectors = page.get("vectors", [])
@@ -325,16 +316,12 @@ async def query_pages(
buffer.extend(vectors)
while (
- len(buffer) >= effective_page_size
- and yielded < top_k
+ yielded < top_k
+ and len(buffer) >= min(effective_page_size, top_k - yielded)
):
- chunk = buffer[:effective_page_size]
- buffer = buffer[effective_page_size:]
-
- remaining = top_k - yielded
-
- if len(chunk) > remaining:
- chunk = chunk[:remaining]
+ chunk_size = min(effective_page_size, top_k - yielded)
+ chunk = buffer[:chunk_size]
+ buffer = buffer[chunk_size:]
yield chunk
yielded += len(chunk)
@@ -342,8 +329,10 @@ async def query_pages(
if yielded >= top_k:
return
- if page.get("NextToken") is None:
+ next_token = page.get("nextToken")
+ if not next_token:
break
+ kwargs["nextToken"] = next_token
if (
effective_page_size is not None
diff --git a/src/dynavec/stores/s3vectors.py b/src/dynavec/stores/s3vectors.py
index 79fb276..7de1bdc 100644
--- a/src/dynavec/stores/s3vectors.py
+++ b/src/dynavec/stores/s3vectors.py
@@ -195,7 +195,7 @@ def query_pages(
return_distance: bool = True,
page_size: int | None = None,
) -> Iterator[list[dict[str, Any]]]:
- """Yield result pages as the S3 Vectors paginator returns them.
+ """Yield result pages from S3 Vectors, following ``nextToken``.
This is what powers streaming search: an agent can begin consuming the
first page while later pages are still in flight.
@@ -233,24 +233,13 @@ def query_pages(
yielded = 0
buffer: list[dict[str, Any]] = []
- with translate_client_errors():
- paginator = self._client.get_paginator("query_vectors")
- page_iterator = iter(
- paginator.paginate(
- PaginationConfig={"MaxItems": top_k},
- **kwargs,
- )
- )
-
while True:
if self._query_limiter is not None:
self._query_limiter.acquire()
- try:
- with translate_client_errors():
- page = next(page_iterator)
- except StopIteration:
- break
+ # Older supported SDKs expose QueryVectors without a paginator.
+ with translate_client_errors():
+ page = self._client.query_vectors(**kwargs)
vectors = page.get("vectors", [])
@@ -270,14 +259,12 @@ def query_pages(
else:
buffer.extend(vectors)
- while len(buffer) >= effective_page_size and yielded < top_k:
- chunk = buffer[:effective_page_size]
- buffer = buffer[effective_page_size:]
-
- remaining = top_k - yielded
-
- if len(chunk) > remaining:
- chunk = chunk[:remaining]
+ while yielded < top_k and len(buffer) >= min(
+ effective_page_size, top_k - yielded
+ ):
+ chunk_size = min(effective_page_size, top_k - yielded)
+ chunk = buffer[:chunk_size]
+ buffer = buffer[chunk_size:]
yield chunk
yielded += len(chunk)
@@ -285,8 +272,10 @@ def query_pages(
if yielded >= top_k:
return
- if page.get("NextToken") is None:
+ next_token = page.get("nextToken")
+ if not next_token:
break
+ kwargs["nextToken"] = next_token
if effective_page_size is not None and buffer and yielded < top_k:
remaining = top_k - yielded
diff --git a/tests/test_async_s3vectors.py b/tests/test_async_s3vectors.py
index 9a46cba..5c95174 100644
--- a/tests/test_async_s3vectors.py
+++ b/tests/test_async_s3vectors.py
@@ -6,24 +6,14 @@
from typing import Any
from unittest.mock import AsyncMock
+import aioboto3
import pytest
+from aiobotocore.stub import AioStubber
from dynavec.config import DynavecConfig
from dynavec.stores.async_s3vectors import AsyncS3VectorsStore
-class FakeAsyncPaginator:
- def __init__(self, pages: list[dict[str, Any]]) -> None:
- self.pages = pages
- self.calls: list[dict[str, Any]] = []
-
- async def paginate(self, **kwargs: Any):
- self.calls.append(kwargs)
-
- for page in self.pages:
- yield page
-
-
def _config() -> DynavecConfig:
return DynavecConfig(
vector_bucket="test-bucket",
@@ -71,14 +61,15 @@ def __init__(
) -> None:
self.put_calls: list[dict[str, Any]] = []
self.get_calls: list[dict[str, Any]] = []
- self.paginator = FakeAsyncPaginator(pages or [])
+ self.pages = iter(pages or [{"vectors": []}])
+ self.query_calls: list[dict[str, Any]] = []
async def put_vectors(self, **kwargs: Any) -> None:
self.put_calls.append(kwargs)
- def get_paginator(self, name: str) -> FakeAsyncPaginator:
- assert name == "query_vectors"
- return self.paginator
+ async def query_vectors(self, **kwargs: Any) -> dict[str, Any]:
+ self.query_calls.append(kwargs)
+ return next(self.pages)
async def get_vectors(self, **kwargs: Any) -> dict[str, Any]:
self.get_calls.append(kwargs)
@@ -188,7 +179,7 @@ async def test_query_returns_paginated_results():
{"key": f"k{i}", "distance": i * 0.01}
for i in range(100)
],
- "NextToken": "token-1",
+ "nextToken": "token-1",
},
{
"vectors": [
@@ -210,6 +201,26 @@ async def test_query_returns_paginated_results():
assert len(results) == 150
assert results[0]["key"] == "k0"
assert results[-1]["key"] == "k149"
+ assert "nextToken" not in client.query_calls[0]
+ assert client.query_calls[1]["nextToken"] == "token-1"
+
+
+async def test_query_with_real_async_sdk_client():
+ session = aioboto3.Session(
+ aws_access_key_id="testing", aws_secret_access_key="testing", region_name="us-east-1"
+ )
+ vectors = [{"key": "doc-1", "distance": 0.25}]
+
+ async with AsyncS3VectorsStore(_config(), session) as store:
+ with AioStubber(store._client) as stubber:
+ stubber.add_response("query_vectors", {"vectors": vectors}, {
+ "vectorBucketName": "test-bucket", "indexName": "test-index", "topK": 1,
+ "queryVector": {"float32": [1.0, 2.0, 3.0, 4.0]},
+ "returnMetadata": True, "returnDistance": True,
+ })
+
+ assert await store.query([1.0, 2.0, 3.0, 4.0], top_k=1) == vectors
+ stubber.assert_no_pending_responses()
async def test_query_truncates_at_top_k():
@@ -219,7 +230,7 @@ async def test_query_truncates_at_top_k():
{"key": f"k{i}"}
for i in range(100)
],
- "NextToken": "token-1",
+ "nextToken": "token-1",
},
{
"vectors": [
@@ -283,6 +294,49 @@ async def test_query_rejects_excessive_top_k():
)
+@pytest.mark.parametrize("page_size", [None, 2])
+async def test_query_pages_continues_after_empty_page_with_next_token(page_size):
+ client = FakeS3Client([
+ {"vectors": [{"key": "k0"}], "nextToken": "token-1"},
+ {"vectors": [], "nextToken": "token-2"},
+ {"vectors": [{"key": "k1"}, {"key": "k2"}]},
+ ])
+ limiter = AsyncMock()
+
+ async with AsyncS3VectorsStore(_config(), FakeSession(client)) as store:
+ store._query_limiter = limiter
+ pages = [
+ page async for page in store.query_pages(
+ [0.1, 0.2, 0.3, 0.4], top_k=5, page_size=page_size
+ )
+ ]
+
+ assert [hit["key"] for page in pages for hit in page] == ["k0", "k1", "k2"]
+ assert [len(page) for page in pages] == ([1, 2] if page_size is None else [2, 1])
+ assert limiter.acquire_async.await_count == 3
+
+
+async def test_query_pages_stops_at_top_k_with_large_page_size():
+ client = FakeS3Client([
+ {"vectors": [{"key": "k0"}], "nextToken": "token-1"},
+ {"vectors": [{"key": "k1"}, {"key": "k2"}], "nextToken": "unused"},
+ ])
+
+ async with AsyncS3VectorsStore(_config(), FakeSession(client)) as store:
+ pages = [page async for page in store.query_pages(
+ [1.0, 2.0, 3.0, 4.0], top_k=2, page_size=100,
+ filter={"kind": "test"}, return_metadata=False, return_distance=False,
+ )]
+
+ assert pages == [[{"key": "k0"}, {"key": "k1"}]]
+ assert len(client.query_calls) == 2
+ for call in client.query_calls:
+ assert call["topK"] == 2
+ assert call["filter"] == {"kind": "test"}
+ assert call["returnMetadata"] is False
+ assert call["returnDistance"] is False
+
+
async def test_put_batch_uses_rate_limiter():
config = DynavecConfig(
vector_bucket="test-bucket",
@@ -319,11 +373,11 @@ async def test_query_pages_uses_rate_limiter_per_page():
pages = [
{
"vectors": [{"key": "k0"}],
- "NextToken": "token-1",
+ "nextToken": "token-1",
},
{
"vectors": [{"key": "k1"}],
- "NextToken": "token-2",
+ "nextToken": "token-2",
},
{
"vectors": [{"key": "k2"}],
diff --git a/tests/test_logging.py b/tests/test_logging.py
index 90a4311..08c3459 100644
--- a/tests/test_logging.py
+++ b/tests/test_logging.py
@@ -130,10 +130,9 @@ def test_dynamodb_store_structured_logging(caplog):
def test_s3vectors_store_structured_logging(caplog):
session = MagicMock()
client = session.client.return_value
- paginator = client.get_paginator.return_value
- paginator.paginate.return_value = [
- {"vectors": [{"key": "ns#1", "data": {"float32": [0.1] * 8}, "distance": 0.05}]}
- ]
+ client.query_vectors.return_value = {
+ "vectors": [{"key": "ns#1", "data": {"float32": [0.1] * 8}, "distance": 0.05}]
+ }
# 1. Opt-in: When False, no logs
cfg_disabled = DynavecConfig(
diff --git a/tests/test_s3vectors.py b/tests/test_s3vectors.py
index 32e1797..48eb538 100644
--- a/tests/test_s3vectors.py
+++ b/tests/test_s3vectors.py
@@ -1,7 +1,10 @@
import time
+from contextlib import closing
from unittest.mock import MagicMock
+import boto3
import pytest
+from botocore.stub import Stubber
from dynavec.config import DynavecConfig
from dynavec.stores.s3vectors import _MAX_TOP_K, S3VectorsStore
@@ -49,34 +52,50 @@ def _store_with_pages(pages, config=None):
store._put_limiter = None
store._query_limiter = None
- paginator = MagicMock()
- paginator.paginate.return_value = pages
- store._client.get_paginator.return_value = paginator
- return store, paginator
+ store._client.query_vectors.side_effect = pages or [{"vectors": []}]
+ return store, store._client.query_vectors
def test_query_single_page():
page = [{"key": f"k{i}", "distance": 0.01 * i} for i in range(50)]
- store, paginator = _store_with_pages([{"vectors": page}])
+ store, query = _store_with_pages([{"vectors": page}])
results = store.query(query_vector=[0.1, 0.2, 0.3, 0.4], top_k=50)
assert len(results) == 50
assert results[0]["key"] == "k0"
assert results[-1]["key"] == "k49"
- store._client.get_paginator.assert_called_once_with("query_vectors")
- assert paginator.paginate.call_args.kwargs["PaginationConfig"] == {"MaxItems": 50}
- assert paginator.paginate.call_args.kwargs["topK"] == 50
+ store._client.get_paginator.assert_not_called()
+ assert query.call_args.kwargs["topK"] == 50
+ assert "nextToken" not in query.call_args.kwargs
+
+
+def test_query_with_real_sdk_client():
+ session = boto3.Session(
+ aws_access_key_id="testing", aws_secret_access_key="testing", region_name="us-east-1"
+ )
+ store = S3VectorsStore(_config(), session)
+ vectors = [{"key": "doc-1", "distance": 0.25}]
+
+ with closing(store._client), Stubber(store._client) as stubber:
+ stubber.add_response("query_vectors", {"vectors": vectors}, {
+ "vectorBucketName": "test-bucket", "indexName": "test-index", "topK": 1,
+ "queryVector": {"float32": [1.0, 2.0, 3.0, 4.0]},
+ "returnMetadata": True, "returnDistance": True,
+ })
+
+ assert store.query([1.0, 2.0, 3.0, 4.0], top_k=1) == vectors
+ stubber.assert_no_pending_responses()
def test_query_multi_page_pagination():
pages = [
{
"vectors": [{"key": f"k{i}"} for i in range(100)],
- "NextToken": "token-1",
+ "nextToken": "token-1",
},
{
"vectors": [{"key": f"k{i}"} for i in range(100, 200)],
- "NextToken": "token-2",
+ "nextToken": "token-2",
},
{
"vectors": [{"key": f"k{i}"} for i in range(200, 250)],
@@ -97,6 +116,9 @@ def test_query_multi_page_pagination():
"k248",
"k249",
]
+ calls = store._client.query_vectors.call_args_list
+ assert "nextToken" not in calls[0].kwargs
+ assert [call.kwargs["nextToken"] for call in calls[1:]] == ["token-1", "token-2"]
def test_query_fewer_results_than_top_k():
store, _ = _store_with_pages([{"vectors": [{"key": "k0"}, {"key": "k1"}]}])
@@ -111,7 +133,7 @@ def test_query_zero_or_negative_top_k():
assert store.query(query_vector=[0.1, 0.2, 0.3, 0.4], top_k=0) == []
assert store.query(query_vector=[0.1, 0.2, 0.3, 0.4], top_k=-5) == []
- store._client.get_paginator.assert_not_called()
+ store._client.query_vectors.assert_not_called()
def test_query_exceeding_service_limit():
@@ -200,7 +222,7 @@ def test_query_truncation_at_top_k():
pages = [
{
"vectors": [{"key": f"k{i}"} for i in range(100)],
- "NextToken": "token-1",
+ "nextToken": "token-1",
},
{
"vectors": [{"key": f"k{i}"} for i in range(100, 200)],
@@ -222,7 +244,7 @@ def test_query_pages_raw_chunks():
pages = [
{
"vectors": [{"key": f"k{i}"} for i in range(100)],
- "NextToken": "token-1",
+ "nextToken": "token-1",
},
{
"vectors": [{"key": f"k{i}"} for i in range(100, 150)],
@@ -273,15 +295,52 @@ def test_query_pages_uses_config_top_k_page_size():
assert [len(p) for p in result_pages] == [10, 10, 5]
+@pytest.mark.parametrize("page_size", [None, 2])
+def test_query_pages_continues_after_empty_page_with_next_token(page_size):
+ store, _ = _store_with_pages([
+ {"vectors": [{"key": "k0"}], "nextToken": "token-1"},
+ {"vectors": [], "nextToken": "token-2"},
+ {"vectors": [{"key": "k1"}, {"key": "k2"}]},
+ ])
+ limiter = MagicMock()
+ store._query_limiter = limiter
+
+ pages = list(store.query_pages([0.1, 0.2, 0.3, 0.4], top_k=5, page_size=page_size))
+
+ assert [hit["key"] for page in pages for hit in page] == ["k0", "k1", "k2"]
+ assert [len(page) for page in pages] == ([1, 2] if page_size is None else [2, 1])
+ assert limiter.acquire.call_count == 3
+
+
+def test_query_pages_stops_at_top_k_with_large_page_size():
+ store, query = _store_with_pages([
+ {"vectors": [{"key": "k0"}], "nextToken": "token-1"},
+ {"vectors": [{"key": "k1"}, {"key": "k2"}], "nextToken": "unused"},
+ ])
+
+ pages = list(store.query_pages(
+ [1.0, 2.0, 3.0, 4.0], top_k=2, page_size=100,
+ filter={"kind": "test"}, return_metadata=False, return_distance=False,
+ ))
+
+ assert pages == [[{"key": "k0"}, {"key": "k1"}]]
+ assert query.call_count == 2
+ for call in query.call_args_list:
+ assert call.kwargs["topK"] == 2
+ assert call.kwargs["filter"] == {"kind": "test"}
+ assert call.kwargs["returnMetadata"] is False
+ assert call.kwargs["returnDistance"] is False
+
+
def test_query_pages_uses_rate_limiter_per_page():
pages = [
{
"vectors": [{"key": "k0"}],
- "NextToken": "token-1",
+ "nextToken": "token-1",
},
{
"vectors": [{"key": "k1"}],
- "NextToken": "token-2",
+ "nextToken": "token-2",
},
{
"vectors": [{"key": "k2"}],
@@ -324,4 +383,4 @@ def test_query_pages_does_not_rate_limit_after_paginator_exhaustion():
)
assert [len(page) for page in result_pages] == [1]
- assert limiter.acquire.call_count == 1
\ No newline at end of file
+ assert limiter.acquire.call_count == 1
diff --git a/tests/test_s3vectors_errors.py b/tests/test_s3vectors_errors.py
index 5d7a2b7..2da1d75 100644
--- a/tests/test_s3vectors_errors.py
+++ b/tests/test_s3vectors_errors.py
@@ -42,6 +42,7 @@ def _async_store():
client = MagicMock()
client.put_vectors = AsyncMock()
client.get_vectors = AsyncMock()
+ client.query_vectors = AsyncMock()
session = MagicMock()
session.client.return_value.__aenter__.return_value = client
return AsyncS3VectorsStore(_config(), session), client
@@ -118,6 +119,7 @@ def test_new_exceptions_are_public(error_type):
("_put_batch", ([],), "put_vectors"),
("get_vectors", (["key"],), "get_vectors"),
("delete_vectors", (["key"],), "delete_vectors"),
+ ("query", ([0.1, 0.2], 2), "query_vectors"),
],
)
def test_sync_direct_operations_translate(
@@ -138,7 +140,11 @@ def test_sync_direct_operations_translate(
@pytest.mark.parametrize("code,error_type", MAPPINGS)
@pytest.mark.parametrize(
"operation,args,aws_operation",
- [("_put_batch", ([],), "put_vectors"), ("get_vectors", (["key"],), "get_vectors")],
+ [
+ ("_put_batch", ([],), "put_vectors"),
+ ("get_vectors", (["key"],), "get_vectors"),
+ ("query", ([0.1, 0.2], 2), "query_vectors"),
+ ],
)
async def test_async_direct_operations_translate(code, error_type, operation, args, aws_operation):
store, client = _async_store()
@@ -155,24 +161,24 @@ async def test_async_direct_operations_translate(code, error_type, operation, ar
def _failing_pages(original):
- yield {"vectors": [{"key": "first"}], "NextToken": "next"}
+ yield {"vectors": [{"key": "first"}], "nextToken": "next"}
raise original
-async def _async_pages(original=None):
- yield {"vectors": [{"key": "first"}], "NextToken": "next"}
- if original is not None:
- raise original
-
-
@pytest.mark.parametrize("operation,args", [("query_pages", ([0.1, 0.2], 2)), ("list_pages", ())])
def test_sync_pagination_translates_later_page_errors(sync_store, operation, args):
store, client = sync_store
original = _client_error("NotFoundException")
- client.get_paginator.return_value.paginate.return_value = _failing_pages(original)
+ if operation == "query_pages":
+ client.query_vectors.side_effect = [
+ {"vectors": [{"key": "first"}], "nextToken": "next"}, original,
+ ]
+ else:
+ client.get_paginator.return_value.paginate.return_value = _failing_pages(original)
pages = getattr(store, operation)(*args)
client.get_paginator.assert_not_called()
+ client.query_vectors.assert_not_called()
assert next(pages) == [{"key": "first"}]
with pytest.raises(NotFoundError) as caught:
next(pages)
@@ -183,11 +189,13 @@ def test_sync_pagination_translates_later_page_errors(sync_store, operation, arg
async def test_async_pagination_translates_later_page_errors():
store, client = _async_store()
original = _client_error("NotFoundException")
- client.get_paginator.return_value.paginate.return_value = _async_pages(original)
+ client.query_vectors.side_effect = [
+ {"vectors": [{"key": "first"}], "nextToken": "next"}, original,
+ ]
async with store:
pages = store.query_pages([0.1, 0.2], 2)
- client.get_paginator.assert_not_called()
+ client.query_vectors.assert_not_called()
assert await pages.__anext__() == [{"key": "first"}]
with pytest.raises(NotFoundError) as caught:
await pages.__anext__()
@@ -195,9 +203,8 @@ async def test_async_pagination_translates_later_page_errors():
assert caught.value.__cause__ is original
-@pytest.mark.parametrize("operation,args", [("query", ([0.1, 0.2], 2)), ("list_pages", ())])
@pytest.mark.parametrize("boundary", ["get_paginator", "paginate"])
-def test_sync_paginator_setup_errors_translate(sync_store, operation, args, boundary):
+def test_sync_list_paginator_setup_errors_translate(sync_store, boundary):
store, client = sync_store
original = _client_error("ValidationException")
call = (
@@ -208,26 +215,7 @@ def test_sync_paginator_setup_errors_translate(sync_store, operation, args, boun
call.side_effect = original
with pytest.raises(ValidationError) as caught:
- list(getattr(store, operation)(*args))
-
- assert caught.value.__cause__ is original
- call.assert_called_once()
-
-
-@pytest.mark.parametrize("boundary", ["get_paginator", "paginate"])
-async def test_async_paginator_setup_errors_translate(boundary):
- store, client = _async_store()
- original = _client_error("ValidationException")
- call = (
- client.get_paginator
- if boundary == "get_paginator"
- else client.get_paginator.return_value.paginate
- )
- call.side_effect = original
-
- async with store:
- with pytest.raises(ValidationError) as caught:
- await store.query([0.1, 0.2], 2)
+ list(store.list_pages())
assert caught.value.__cause__ is original
call.assert_called_once()
@@ -287,32 +275,36 @@ async def test_async_batch_retries_then_succeeds(monkeypatch, code):
def test_sync_query_retries_page_iteration_failure(sync_store, monkeypatch):
store, client = sync_store
- client.get_paginator.return_value.paginate.side_effect = [
- _failing_pages(_client_error("ThrottlingException")),
- [{"vectors": [{"key": "success"}]}],
+ client.query_vectors.side_effect = [
+ {"vectors": [{"key": "first"}], "nextToken": "next"},
+ _client_error("ThrottlingException"),
+ {"vectors": [{"key": "success"}]},
]
sleep = MagicMock()
monkeypatch.setattr("dynavec.utils.time.sleep", sleep)
assert store.query([0.1, 0.2], 2) == [{"key": "success"}]
- assert client.get_paginator.call_count == 2
+ assert client.query_vectors.call_count == 3
+ assert "nextToken" not in client.query_vectors.call_args.kwargs
sleep.assert_called_once()
async def test_async_query_retries_page_iteration_failure(monkeypatch):
store, client = _async_store()
- client.get_paginator.return_value.paginate.side_effect = [
- _async_pages(_client_error("ThrottlingException")),
- _async_pages(),
+ client.query_vectors.side_effect = [
+ {"vectors": [{"key": "first"}], "nextToken": "next"},
+ _client_error("ThrottlingException"),
+ {"vectors": [{"key": "success"}]},
]
sleep = AsyncMock()
monkeypatch.setattr("dynavec.utils.asyncio.sleep", sleep)
async with store:
- assert await store.query([0.1, 0.2], 2) == [{"key": "first"}]
+ assert await store.query([0.1, 0.2], 2) == [{"key": "success"}]
- assert client.get_paginator.call_count == 2
+ assert client.query_vectors.await_count == 3
+ assert "nextToken" not in client.query_vectors.call_args.kwargs
sleep.assert_awaited_once()
From d7b1531e8c980e1c98ce60bc484d54fbd1531de1 Mon Sep 17 00:00:00 2001
From: Lawliet2004 <168631200+Lawliet2004@users.noreply.github.com>
Date: Fri, 6 Nov 2026 20:03:00 +0530
Subject: [PATCH 2/3] docs: audit open issues and prioritize resolution work
---
docs/ISSUE_AUDIT_2026-11-06.md | 165 +++++++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
create mode 100644 docs/ISSUE_AUDIT_2026-11-06.md
diff --git a/docs/ISSUE_AUDIT_2026-11-06.md b/docs/ISSUE_AUDIT_2026-11-06.md
new file mode 100644
index 0000000..d541a9c
--- /dev/null
+++ b/docs/ISSUE_AUDIT_2026-11-06.md
@@ -0,0 +1,165 @@
+# Issue audit — 6 November 2026
+
+## Scope and evidence
+
+Audit baseline: `development` at `b9c90d7`. GitHub inventory was read on 6 November 2026: **192 issues, 79 open and 113 closed; 158 pull requests, 7 open** before this audit PR. Every open issue appears exactly once in the inventory below. Closed issues were sampled against relevant code and PRs; this is not a reproduction of every closed issue.
+
+Assessments compare issue acceptance criteria, current source/tests, and merged/open PR metadata. A linked or closed PR is not proof that a feature shipped. In particular, the comment on #29 links provisioning-test PR #315, not an edge-storage implementation. Static code presence is not an end-to-end production guarantee.
+
+[Issue tracker](https://github.com/codeforstartups/dynavec/issues) · [Pull requests](https://github.com/codeforstartups/dynavec/pulls) · [Contribution/assignment policy](../CONTRIBUTING.md#finding--claiming-issues). Ownership is the snapshot assignee shown on each row; “unassigned” is not an assignment. Coordinate existing work before claiming an issue. This PR fixes a regression discovered during the audit rather than implementing another contributor's assigned feature.
+
+| Assessment | Open issues | Meaning |
+|---|---:|---|
+| Review closure | 11 | Implementation evidence exists; maintainer acceptance or a narrow follow-up is still required. |
+| Partial | 28 | Some requested behavior exists, with explicit gaps listed below. |
+| Open PR | 4 | Implementation is being proposed; review/merge and acceptance checks remain. |
+| Missing | 31 | The requested public flow or essential deliverable was not found in the inspected code. |
+| Epic | 5 | Parent tracking issue; resolve its children rather than treating it as one patch. |
+
+Priorities are audit recommendations: **P1** correctness/security or a blocking integration; **P2** functional completion; **P3** documentation, polish or later architecture. They are not repository labels.
+
+## Recommended order
+
+| Order | Work | Completion check |
+|---|---|---|
+| 1 | Merge the QueryVectors fix in this PR. | Real boto3/aioboto3 stubs work without a query paginator; all pages are consumed using `nextToken`; top-k, buffering, errors and rate limiting remain correct. |
+| 2 | Coordinate correctness/security gaps #8, #28, #97, #100, #137 and #342 with their owners. | Cross-model caches are isolated; partial writes have a repair strategy; sample IAM is scoped; formatted secrets are redacted; dashboard APIs enforce their access model; routing is invoked once. |
+| 3 | Review pending PRs: #348 → #349, #351, and draft #316. | Verify acceptance cases before merging; retire superseded #332/#336 in coordination with their authors. |
+| 4 | Review the closure candidates below and reconcile #130/#141. | Update issue state only after maintainer acceptance; retain specific remaining gaps as follow-ups. |
+| 5 | Finish persistent telemetry/run history and the shared graph schema. | #127/#128/#299/#301/#302 and #282/#284 provide stable data contracts before building dependent UI. |
+| 6 | Build visual/eval/deployment flows on those contracts. | Dataset/gate/run links, canvas round-trip and deployment each have an executable acceptance scenario; expand to GCP/Azure afterward. |
+
+## Confirmed defects found while auditing
+
+### Selected fix: S3 Vectors queries stop early or cannot start
+
+Both stores requested a `query_vectors` paginator. The environment produced by `make install` resolves boto3/botocore **1.40.61**, where `client.can_paginate("query_vectors")` is false and `get_paginator` raises `OperationNotPageableError`. Newer AWS APIs support continuation, but both stores checked `NextToken` instead of the documented **`nextToken`**. This silently discarded later results. Earlier tests used the same incorrect uppercase key.
+
+The fix calls `query_vectors` directly in sync and async stores and forwards a nonempty `nextToken`. This supports a single response on older SDKs and continuation on SDK/service versions exposing it, without changing dependency constraints. A caller still needs an SDK supporting any request fields/features it uses; this change does not add pagination support to an old service model.
+
+Regression coverage includes real SDK clients with Stubber/AioStubber, multiple service pages, empty intermediate pages, client chunk buffering (including a page size larger than top-k), cursor forwarding, top-k limits, one rate-limit acquisition per request, permanent error translation and retry restart. Tests with realistic lowercase tokens reproduced 15 failures before the production fix. The historical pagination issue [#16](https://github.com/codeforstartups/dynavec/issues/16) is closed, so its tracker state alone did not establish correctness.
+
+Sources: [AWS QueryVectors request/response](https://docs.aws.amazon.com/AmazonS3/latest/API/API_S3VectorBuckets_QueryVectors.html), [boto3 query_vectors](https://docs.aws.amazon.com/boto3/latest/reference/services/s3vectors/client/query_vectors.html), and the installed SDK service/paginator model inspected offline.
+
+### Follow-ups reproduced without external services
+
+- **#8:** two CachedEmbedder instances of the same backend class, with different model identities and a shared cache, reuse the first model's vector under the default class-based key. Use explicit distinct `model_key` values as the current workaround; a full fix must encode actual embedding identity.
+- **#342:** one Supervisor.run call invokes a configured router **twice**, as shown by a counting router. A stateful router can produce different selections between validation/execution. Reuse the first selected route and add an invocation-count regression.
+
+These findings are resolution work for the relevant issues, not claims that those issues are fixed by this PR. Security-related reproduction details are excluded from this public report in accordance with [SECURITY.md](../SECURITY.md).
+
+## Open-issue inventory
+
+Each row states source evidence and a concrete next action. “Review closure” is a recommendation for maintainer review, not an automatic closure request. UI acceptance and cloud deployment checks remain separate from offline Python tests.
+
+### Review closure (11)
+
+| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
+|---|---|---|---|
+| [#7 — Retry/backoff for OpenAI embedder rate limits](https://github.com/codeforstartups/dynavec/issues/7)
@tikhandesanket | P2 | [src/dynavec/embeddings/openai.py](../src/dynavec/embeddings/openai.py)
[tests/test_openai_embeddings.py](../tests/test_openai_embeddings.py)
[PR #242 merged](https://github.com/codeforstartups/dynavec/pull/242) | 429/5xx retries and numeric Retry-After handling exist. Compare the tested header formats with the intended acceptance criteria, then close or narrow the issue. |
+| [#39 — TTL jitter for DynamoDBCache](https://github.com/codeforstartups/dynavec/issues/39)
@be-student | P3 | [src/dynavec/cache.py](../src/dynavec/cache.py)
[tests/test_cache.py](../tests/test_cache.py)
[tools/build_docs.py](../tools/build_docs.py) | DynamoDBCache has bounded configurable TTL jitter, validation, deterministic tests and a caching guide. Review the existing behavior and close; a closed-unmerged PR alone would not establish completion. |
+| [#43 — Cache warmup utility](https://github.com/codeforstartups/dynavec/issues/43)
@arshsk16 | P3 | [src/dynavec/cache.py](../src/dynavec/cache.py)
[tests/test_cache.py](../tests/test_cache.py)
[CHANGELOG.md](../CHANGELOG.md) | warm_cache(client, queries) already populates a configured cache and has tests. Confirm the accepted public name relative to warm(queries), finish any dedicated usage docs, then close or narrow. |
+| [#69 — Semantic Kernel memory connector](https://github.com/codeforstartups/dynavec/issues/69)
@tikhandesanket | P2 | [src/dynavec/integrations/semantic_kernel.py](../src/dynavec/integrations/semantic_kernel.py)
[tests/test_semantic_kernel.py](../tests/test_semantic_kernel.py)
[examples/semantic_kernel_vector_store.py](../examples/semantic_kernel_vector_store.py) | A Semantic Kernel vector-store adapter, example and tests exist. Confirm that this fulfills the intended memory connector API on supported Semantic Kernel versions; close or specify the remaining interface gap. |
+| [#75 — Optimized Product Quantization (OPQ)](https://github.com/codeforstartups/dynavec/issues/75)
@tikhandesanket | P2 | [src/dynavec/quantization.py](../src/dynavec/quantization.py)
[tests/test_quantization.py](../tests/test_quantization.py)
[benchmarks/benchmark_opq.py](../benchmarks/benchmark_opq.py) | OPQRotation and OptimizedProductQuantizer, tests and a recall benchmark exist. Review benchmark methodology/results against the acceptance criteria and close or narrow; PR #198 being closed is not sufficient evidence on its own. |
+| [#79 — Hot/cold sync on writes](https://github.com/codeforstartups/dynavec/issues/79)
@Isha-Zaka | P2 | [src/dynavec/client.py](../src/dynavec/client.py)
[tests/test_hot_tier.py](../tests/test_hot_tier.py) | The sync client fans successful writes/deletes into the hot tier, with write-through and delete consistency tests. Verify the intended scope, then close; failure recovery across stores remains separate work under #28. |
+| [#103 — Cookbook: production RAG with namespaces](https://github.com/codeforstartups/dynavec/issues/103)
@tikhandesanket | P3 | [examples/multi_tenant_rag.py](../examples/multi_tenant_rag.py)
[tools/build_docs.py](../tools/build_docs.py)
[PR #239 merged](https://github.com/codeforstartups/dynavec/pull/239) | The multi-tenant RAG example and namespaces documentation exist. Review the recipe and tenant-authentication guidance against production expectations, then close or narrow. |
+| [#138 — Dashboard: dark mode + theme parity with the landing page](https://github.com/codeforstartups/dynavec/issues/138)
@tikhandesanket | P3 | [dashboard/components/ThemeToggle.tsx](../dashboard/components/ThemeToggle.tsx)
[dashboard/app/layout.tsx](../dashboard/app/layout.tsx)
[dashboard/app/globals.css](../dashboard/app/globals.css)
[PR #187 merged](https://github.com/codeforstartups/dynavec/pull/187) | Dark palette and persisted theme toggle are implemented. Capture the requested parity screenshots and obtain design acceptance before closing; no new theme implementation is needed. |
+| [#176 — dynavec complete landing page revamp](https://github.com/codeforstartups/dynavec/issues/176)
@tikhandesanket | P3 | [opensource/dynavec/index.html](../opensource/dynavec/index.html)
[opensource/dynavec/styles.css](../opensource/dynavec/styles.css)
[PR #347 merged](https://github.com/codeforstartups/dynavec/pull/347) | The latest merged PR redesigns the landing page and docs. Request visual acceptance of that implementation and close or list concrete remaining defects; avoid starting another full redesign. |
+| [#310 — Landing page: "How they fit together" diagram](https://github.com/codeforstartups/dynavec/issues/310)
unassigned | P3 | [opensource/dynavec/index.html](../opensource/dynavec/index.html)
[opensource/dynavec/styles.css](../opensource/dynavec/styles.css)
[PR #347 merged](https://github.com/codeforstartups/dynavec/pull/347) | The landing page now includes a four-stage ecosystem cycle and SVG artwork. Verify the single flow/caption and desktop/mobile readability against the issue, then close or specify the visual gap. |
+| [#311 — Landing page: positioning vs LangChain / LangGraph (parity + differentiators)](https://github.com/codeforstartups/dynavec/issues/311)
unassigned | P3 | [opensource/dynavec/index.html](../opensource/dynavec/index.html)
[docs/roadmap.md](../docs/roadmap.md)
[PR #347 merged](https://github.com/codeforstartups/dynavec/pull/347) | The landing page includes a capability comparison with availability markers. Cross-check every status against the actual SDK/visual/deployment flows and approve the wording before closure. |
+
+### Open PR (4)
+
+| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
+|---|---|---|---|
+| [#40 — Redis Cluster / TLS support](https://github.com/codeforstartups/dynavec/issues/40)
@sriram1900 | P2 | [src/dynavec/cache.py](../src/dynavec/cache.py)
[PR #316 open](https://github.com/codeforstartups/dynavec/pull/316) | PR #316 describes itself as work in progress. Coordinate with its author to add cluster-client selection, TLS options and docs, then run backend parity tests. Keep the issue open until implementation is merged. |
+| [#290 — Conversation / short-term memory store on DynamoDB](https://github.com/codeforstartups/dynavec/issues/290)
@shivamm-gupta | P2 | [PR #349 open](https://github.com/codeforstartups/dynavec/pull/349)
[PR #336 open](https://github.com/codeforstartups/dynavec/pull/336)
[src/dynavec/agents/react.py](../src/dynavec/agents/react.py) | Review #348 first, then the maintainer port #349 stacked on it. The #349 description supersedes #336; avoid another implementation and verify multi-turn memory, trimming, restart and interrupt/resume together. |
+| [#296 — Human-in-the-loop / interrupts](https://github.com/codeforstartups/dynavec/issues/296)
@Uzmaa7 | P1 | [PR #348 open](https://github.com/codeforstartups/dynavec/pull/348)
[PR #332 open](https://github.com/codeforstartups/dynavec/pull/332)
[src/dynavec/agents/react.py](../src/dynavec/agents/react.py) | Review maintainer port #348, which supersedes #332. Verify approve/reject, provider-valid tool message ordering and restart-safe resume; separately verify the issue's timeout/expiry criterion before closure. |
+| [#305 — Regression runs & CI gating (fail when a metric drops)](https://github.com/codeforstartups/dynavec/issues/305)
unassigned | P1 | [src/dynavec/eval/trend.py](../src/dynavec/eval/trend.py)
[PR #351 open](https://github.com/codeforstartups/dynavec/pull/351) | Review #351 instead of rebuilding its gate. Verify threshold polarity, zero/NaN/missing metric handling, persisted baselines, CLI exit codes and the GitHub Actions example; the unmerged PR does not resolve the issue. |
+
+### Partial (28)
+
+| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
+|---|---|---|---|
+| [#8 — Optional embedding cache to avoid re-embedding identical text](https://github.com/codeforstartups/dynavec/issues/8)
@tikhandesanket | P1 | [src/dynavec/embeddings/cached.py](../src/dynavec/embeddings/cached.py)
[src/dynavec/embeddings/cache.py](../src/dynavec/embeddings/cache.py)
[tests/test_embedding_cache.py](../tests/test_embedding_cache.py) | Caching exists, but the default model key is the embedder class name. Include provider/model/dimension identity or require an explicit key when sharing a cache; test two models of the same class sharing one backend. |
+| [#26 — Support provisioned billing + autoscaling docs](https://github.com/codeforstartups/dynavec/issues/26)
unassigned | P3 | [src/dynavec/provisioning.py](../src/dynavec/provisioning.py)
[src/dynavec/config.py](../src/dynavec/config.py)
[ARCHITECTURE.md](../ARCHITECTURE.md) | PROVISIONED mode already creates 5 RCU/5 WCU. Document capacity planning, application autoscaling setup/IAM and an example config; distinguish the current fixed defaults from automatic scaling. |
+| [#51 — Client-side rate limiting / backpressure](https://github.com/codeforstartups/dynavec/issues/51)
@GenZ-CODER-X | P2 | [src/dynavec/utils.py](../src/dynavec/utils.py)
[src/dynavec/config.py](../src/dynavec/config.py)
[src/dynavec/stores/s3vectors.py](../src/dynavec/stores/s3vectors.py)
[tests/test_s3vectors.py](../tests/test_s3vectors.py)
[tests/test_async_s3vectors.py](../tests/test_async_s3vectors.py) | TokenBucket, put_rps/query_rps and sync/async hooks already exist. Add deterministic burst/refill and concurrent contention tests, document limits, and audit remaining put/query paths before closure. |
+| [#62 — Incremental / delta ingestion](https://github.com/codeforstartups/dynavec/issues/62)
@Pratham1603 | P2 | [src/dynavec/ingest.py](../src/dynavec/ingest.py)
[src/dynavec/metadata.py](../src/dynavec/metadata.py) | Within-call deduplication exists, but there is no stored-hash comparison across runs. Add an opt-in per-chunk skip_unchanged path, preserve unconditional overwrite by default, and test changed metadata/text plus records without hashes. |
+| [#64 — Ingest MCP tools & prompts primitives](https://github.com/codeforstartups/dynavec/issues/64)
@GenZ-CODER-X | P2 | [src/dynavec/ingest.py](../src/dynavec/ingest.py)
[src/dynavec/agents/connectors.py](../src/dynavec/agents/connectors.py) | MCPResourceSource ingests resources and agent connectors expose tools, but ingestion adapters for tool/prompt definitions are absent. Add duck-typed source adapters and fake-session tests without executing tools. |
+| [#85 — dynavec CLI: provision/upsert/query/stats](https://github.com/codeforstartups/dynavec/issues/85)
unassigned | P2 | [src/dynavec/cli.py](../src/dynavec/cli.py)
[tests/test_cli.py](../tests/test_cli.py)
[pyproject.toml](../pyproject.toml) | The CLI already has version/doctor/export/import/mcp. Add provision/upsert/query/stats commands, explicit config/.env precedence and offline parser/error tests. |
+| [#89 — moto-based DynamoDB integration tests](https://github.com/codeforstartups/dynavec/issues/89)
unassigned | P2 | [tests/test_provisioning.py](../tests/test_provisioning.py)
[tests/test_dynamodb.py](../tests/test_dynamodb.py)
[tests/test_dynamodb_ttl.py](../tests/test_dynamodb_ttl.py)
[tests/test_async_dynamodb_moto.py](../tests/test_async_dynamodb_moto.py)
[.github/workflows/ci.yml](../.github/workflows/ci.yml) | Moto provisioning/versioning/TTL and async round-trip tests already run through pytest in CI. Complete explicit batch put/get/delete round-trips and failure cases, then close or narrow instead of adding a duplicate suite. |
+| [#93 — Credentials assume-role test](https://github.com/codeforstartups/dynavec/issues/93)
unassigned | P2 | [src/dynavec/credentials.py](../src/dynavec/credentials.py)
[tests/test_credentials.py](../tests/test_credentials.py) | The async test mocks AWSCredentials.session(), bypassing STS. Stub STS assume_role directly and assert role/session/external ID forwarding and temporary credentials, including failures and source-profile region behavior. |
+| [#95 — Release automation notes / changelog](https://github.com/codeforstartups/dynavec/issues/95)
unassigned | P3 | [CHANGELOG.md](../CHANGELOG.md)
[.github/workflows/publish.yml](../.github/workflows/publish.yml)
[tools/build_docs.py](../tools/build_docs.py) | Changelog, release publishing workflow and release notes already exist. Add a maintainer release checklist covering version/tag consistency, build checks and verification of the published package. |
+| [#96 — Coverage + PyPI + CI badges in README](https://github.com/codeforstartups/dynavec/issues/96)
unassigned | P3 | [README.md](../README.md)
[opensource/dynavec/index.html](../opensource/dynavec/index.html)
[.github/workflows/ci.yml](../.github/workflows/ci.yml) | README PyPI/CI badges exist; coverage publishing and a coverage badge are absent. Define the coverage source/threshold, publish its report and verify all requested badge links on both surfaces. |
+| [#99 — KMS encryption docs for table + bucket](https://github.com/codeforstartups/dynavec/issues/99)
unassigned | P2 | [ARCHITECTURE.md](../ARCHITECTURE.md)
[docs/iam-policy.json](../docs/iam-policy.json)
[src/dynavec/provisioning.py](../src/dynavec/provisioning.py) | Encryption has a brief architecture note, but no SSE-KMS recipe or key-policy guidance. Document table/bucket creation settings, required KMS/IAM permissions and an example; do not imply provisioning currently accepts KMS configuration. |
+| [#100 — Redact secrets from any logging/errors](https://github.com/codeforstartups/dynavec/issues/100)
@tenten48tenten | P1 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[tests/test_logging.py](../tests/test_logging.py)
[tests/test_run_logging.py](../tests/test_run_logging.py) | Redaction helpers and tests exist. Complete an audit and synthetic-secret coverage across messages, tracebacks and AWS/provider errors; coordinate remediation with the assignee and follow SECURITY.md for any vulnerability details. |
+| [#101 — Auto-generate API reference (pdoc)](https://github.com/codeforstartups/dynavec/issues/101)
@raunak3208 | P3 | [tools/build_docs.py](../tools/build_docs.py)
[.github/workflows/pages.yml](../.github/workflows/pages.yml) | The static docs are hand-authored in build_docs.py. Add a reproducible pdoc API-reference build/publish step, optional dependency handling and a public-symbol smoke check. |
+| [#128 — Client telemetry hook (opt-in) to record query events](https://github.com/codeforstartups/dynavec/issues/128)
unassigned | P2 | [src/dynavec/client.py](../src/dynavec/client.py)
[src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[tests/test_telemetry.py](../tests/test_telemetry.py) | Search telemetry is opt-in via a recorder and omits query text by default. Add stage timings/result IDs as approved by the privacy contract, and a nonblocking persistent sink tied to #127; test enabled/disabled behavior. |
+| [#132 — Dashboard view: Query analytics (latency, QPS, cache hit-rate)](https://github.com/codeforstartups/dynavec/issues/132)
@raunak3208 | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[dashboard/components/Kpis.tsx](../dashboard/components/Kpis.tsx)
[dashboard/components/LatencyChart.tsx](../dashboard/components/LatencyChart.tsx)
[dashboard/components/VolumeChart.tsx](../dashboard/components/VolumeChart.tsx) | KPIs, percentiles, cache hit-rate and a range selector already exist. Add the requested latency percentile time-series and verify real-data behavior; coordinate with the current assignee. |
+| [#133 — Dashboard view: Per-query drill-down](https://github.com/codeforstartups/dynavec/issues/133)
unassigned | P2 | [dashboard/components/TraceDrawer.tsx](../dashboard/components/TraceDrawer.tsx)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[src/dynavec/client.py](../src/dynavec/client.py) | The query drawer and trace endpoint exist, but the drawer lacks stage waterfalls and result links/IDs. Persist stage timings and approved result details, then test drill-down with text capture both enabled and disabled. |
+| [#136 — Dashboard view: Retrieval-quality trends over time](https://github.com/codeforstartups/dynavec/issues/136)
@tikhandesanket | P2 | [src/dynavec/eval/trend.py](../src/dynavec/eval/trend.py)
[src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx) | EvalRunStore trends/comparison and eval API endpoints exist. Add the dashboard score/trend panel with dataset/model filters and run comparison; backend support alone does not finish the UI issue. |
+| [#137 — Dashboard: security — local-only, server-side creds, API token](https://github.com/codeforstartups/dynavec/issues/137)
unassigned | P1 | [src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/lib/api.ts](../dashboard/lib/api.ts) | The server binds to 127.0.0.1 by default, but APIs have no token check and serve() allows another host. Add a server-side local API authentication/origin model, document it and test unauthorized requests and credential exclusion. |
+| [#281 — Snapshots for resume, time-travel & replay](https://github.com/codeforstartups/dynavec/issues/281)
unassigned | P2 | [src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py)
[src/dynavec/nodes/graph.py](../src/dynavec/nodes/graph.py)
[tests/test_checkpointer.py](../tests/test_checkpointer.py) | Checkpoint history and failed-run resume exist. Add explicit immutable snapshot/fork/replay APIs with input overrides and tests proving historical snapshots stay unchanged. |
+| [#282 — Typed node system / node schema (ReactFlow-compatible)](https://github.com/codeforstartups/dynavec/issues/282)
unassigned | P2 | [src/dynavec/nodes/base.py](../src/dynavec/nodes/base.py)
[src/dynavec/nodes/graph.py](../src/dynavec/nodes/graph.py)
[src/dynavec/nodes/registry.py](../src/dynavec/nodes/registry.py) | Typed port declarations, dictionaries and a canvas palette exist. Add a versioned graph JSON loader/round-trip and validate port compatibility across edges; declarations alone do not enforce types. |
+| [#284 — Custom nodes via the SDK](https://github.com/codeforstartups/dynavec/issues/284)
unassigned | P2 | [src/dynavec/nodes/base.py](../src/dynavec/nodes/base.py)
[src/dynavec/nodes/registry.py](../src/dynavec/nodes/registry.py)
[tests/test_nodes.py](../tests/test_nodes.py) | Node subclassing/registration is already possible. Document a public custom-node example, execute it in CI and verify graph serialization/restoration through the shared schema from #282. |
+| [#295 — SDK & CLI: run / deploy / inspect with code⇄visual parity](https://github.com/codeforstartups/dynavec/issues/295)
unassigned | P2 | [src/dynavec/agents/graph.py](../src/dynavec/agents/graph.py)
[src/dynavec/cli.py](../src/dynavec/cli.py) | SDK graph execution exists; orchestration run/inspect/deploy CLI and code/canvas parity do not. Land the shared schema first, then add offline-tested commands; deployment also depends on #298. |
+| [#299 — Per-node traces/spans assembled into a run tree](https://github.com/codeforstartups/dynavec/issues/299)
@Isha-Zaka | P2 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[tests/test_run_logging.py](../tests/test_run_logging.py) | Run/node/span correlation IDs exist, but a parented run tree is missing. Add parent_span_id, start/end timing and stable span events, then test nested/parallel propagation and tree assembly. |
+| [#301 — Run history: searchable / filterable list with drill-down to a trace](https://github.com/codeforstartups/dynavec/issues/301)
unassigned | P2 | [src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py)
[src/dynavec/logging.py](../src/dynavec/logging.py)
[dashboard/components/TracesTable.tsx](../dashboard/components/TracesTable.tsx) | The UI lists query events; checkpoints/log sinks hold pieces of agent runs. Add a durable, indexed run-list API plus run detail/tree UI with status/time/model/tag filters and pagination. |
+| [#302 — Metrics: latency percentiles, QPS, cache hit-rate, error rate, token/cost per run](https://github.com/codeforstartups/dynavec/issues/302)
unassigned | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[dashboard/components/Kpis.tsx](../dashboard/components/Kpis.tsx)
[src/dynavec/logging.py](../src/dynavec/logging.py) | Query latency, traffic, cache and error aggregates exist. Add per-run model token/cost accounting, parented run attribution and consistent aggregates; test real usage values and unknown-price behavior. |
+| [#304 — Test cases & datasets (labeled inputs → expected outputs/relevance)](https://github.com/codeforstartups/dynavec/issues/304)
unassigned | P2 | [src/dynavec/eval/retrieval.py](../src/dynavec/eval/retrieval.py)
[src/dynavec/eval/runner.py](../src/dynavec/eval/runner.py)
[tests/test_retrieval_eval.py](../tests/test_retrieval_eval.py) | LabeledQuery, JSON/JSONL loading and per-example/aggregate retrieval scores exist. Add dataset saving/round-trip, tags/expected-output schema and graph-suite support without duplicating the retrieval runner. |
+| [#306 — In-loop eval: attach evals to the same run/trace view](https://github.com/codeforstartups/dynavec/issues/306)
unassigned | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[src/dynavec/eval/trend.py](../src/dynavec/eval/trend.py) | Eval metric fields and endpoints exist, but no complete run/span-tree association and score drill-down UI. Join eval results to execution run IDs and test query-by-run plus dashboard visibility. |
+| [#342 — feat(dynaflow): introduce multi-agent supervisor and handoff primitives](https://github.com/codeforstartups/dynavec/issues/342)
@tikhandesanket | P1 | [src/dynavec/agents/supervisor.py](../src/dynavec/agents/supervisor.py)
[src/dynavec/agents/handoff.py](../src/dynavec/agents/handoff.py)
[src/dynavec/agents/state.py](../src/dynavec/agents/state.py)
[tests/test_supervisor_routing.py](../tests/test_supervisor_routing.py)
[PR #345 merged](https://github.com/codeforstartups/dynavec/pull/345) | Supervisor/state/handoff implementations exist under dynavec. Supervisor.run currently calls its router twice per run: call once and test a stateful router. Separately agree whether the dynaflow package-boundary requirement remains before closure. |
+
+### Missing (31)
+
+| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
+|---|---|---|---|
+| [#17 — Add delete-by-filter helper](https://github.com/codeforstartups/dynavec/issues/17)
unassigned | P2 | [src/dynavec/client.py](../src/dynavec/client.py)
[src/dynavec/namespace.py](../src/dynavec/namespace.py)
[tests/test_list_vectors.py](../tests/test_list_vectors.py) | Define supported filter semantics, enumerate the complete namespace, evaluate matching metadata, and batch delete both stores with cache/hot-tier invalidation. Test more than one batch, empty matches, failures and tenant isolation; ANN top-k is not exhaustive deletion. |
+| [#21 — Optional GSI for metadata access patterns](https://github.com/codeforstartups/dynavec/issues/21)
unassigned | P2 | [src/dynavec/config.py](../src/dynavec/config.py)
[src/dynavec/provisioning.py](../src/dynavec/provisioning.py) | Design explicit metadata access patterns and projected/indexed fields; add opt-in GSI provisioning and a query helper. Cover existing-table migration, pagination and the no-GSI default. |
+| [#28 — Transactional write across both stores (best-effort)](https://github.com/codeforstartups/dynavec/issues/28)
unassigned | P1 | [src/dynavec/client.py](../src/dynavec/client.py)
[src/dynavec/async_client.py](../src/dynavec/async_client.py) | Document the two-store failure model and implement a bounded repair/journal strategy for partial writes. Test failure of either store and retries over existing records; blind deletion as rollback can destroy an earlier version. |
+| [#29 — Sort-key adjacency design for high fan-out nodes](https://github.com/codeforstartups/dynavec/issues/29)
@tikhandesanket | P2 | [src/dynavec/graph.py](../src/dynavec/graph.py)
[PR #315 merged](https://github.com/codeforstartups/dynavec/pull/315) | Adjacency still lives in embedded lists. Propose separate edge items and a migration/backfill path, then benchmark high fan-out and pagination. PR #315 tests table provisioning; it does not implement this issue despite the issue comment linking it. |
+| [#42 — Memcached backend](https://github.com/codeforstartups/dynavec/issues/42)
@GenZ-CODER-X | P3 | [src/dynavec/cache.py](../src/dynavec/cache.py)
[pyproject.toml](../pyproject.toml) | Add an optional Memcached client extra and BaseCache implementation, including namespace invalidation and TTL/stats parity tests. |
+| [#45 — Cohere Rerank API integration](https://github.com/codeforstartups/dynavec/issues/45)
@troublerone | P2 | [src/dynavec/retrieval.py](../src/dynavec/retrieval.py)
[src/dynavec/client.py](../src/dynavec/client.py)
[pyproject.toml](../pyproject.toml) | Add a pluggable hosted reranker with caller-supplied Cohere credentials and an optional extra. Test ordered result mapping, empty inputs and provider errors; existing cross-encoder reranking is a different backend. |
+| [#52 — Thread-safety tests for stores](https://github.com/codeforstartups/dynavec/issues/52)
unassigned | P2 | [tests/test_client_inmemory.py](../tests/test_client_inmemory.py)
[tests/test_connection_pool.py](../tests/test_connection_pool.py) | Add barrier-driven concurrent upsert/search scenarios against the fakes, asserting complete payloads and namespace isolation. Separate fake-store thread safety from guarantees of the AWS-backed client. |
+| [#53 — Process pool for CPU-bound PQ training](https://github.com/codeforstartups/dynavec/issues/53)
@tikhandesanket | P3 | [src/dynavec/quantization.py](../src/dynavec/quantization.py)
[benchmarks/benchmark_quantization.py](../benchmarks/benchmark_quantization.py) | Add opt-in process-based PQ fitting only after benchmarking serialization/startup costs. Keep deterministic seeds, a sequential default and a benchmark demonstrating where the process pool helps. |
+| [#82 — OpenTelemetry tracing](https://github.com/codeforstartups/dynavec/issues/82)
unassigned | P2 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[src/dynavec/client.py](../src/dynavec/client.py)
[pyproject.toml](../pyproject.toml) | Add optional OpenTelemetry spans for embed/query/hydrate/rerank, preserve caller trace context, and supply a collector example. Test span parentage and operation without the extra installed. |
+| [#83 — CloudWatch metrics emission](https://github.com/codeforstartups/dynavec/issues/83)
unassigned | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[src/dynavec/config.py](../src/dynavec/config.py)
[docs/iam-policy.json](../docs/iam-policy.json) | Add an opt-in CloudWatch emitter with batching, bounded buffers and documented IAM. Test dimension/cardinality limits and ensure emitter failure does not fail retrieval. |
+| [#88 — CLI: migrate from Pinecone](https://github.com/codeforstartups/dynavec/issues/88)
@Isha-Zaka | P2 | [src/dynavec/cli.py](../src/dynavec/cli.py)
[examples/pinecone_vs_dynavec.py](../examples/pinecone_vs_dynavec.py) | Add a resumable, batched Pinecone migration command preserving IDs, namespaces, metadata and vector dimensions. Test source pagination and interrupted migration; comparison examples are not an importer. |
+| [#97 — Tighten S3 Vectors resource ARNs in the sample policy](https://github.com/codeforstartups/dynavec/issues/97)
unassigned | P1 | [README.md](../README.md)
[docs/iam-policy.json](../docs/iam-policy.json)
[ARCHITECTURE.md](../ARCHITECTURE.md) | Separate account-level list/create permissions from bucket/index data permissions using the current AWS authorization resource table. Scope data actions to the configured bucket/index and test documented runtime/provisioning policies. |
+| [#102 — Migrating from Pinecone guide](https://github.com/codeforstartups/dynavec/issues/102)
unassigned | P3 | [examples/pinecone_vs_dynavec.py](../examples/pinecone_vs_dynavec.py)
[tools/build_docs.py](../tools/build_docs.py) | Write a migration guide with a concepts table, namespace/metric/filter differences, batched export/import examples and a verification checklist. Keep documentation separate from the importer in #88. |
+| [#114 — Add Python 3.13 to the CI matrix](https://github.com/codeforstartups/dynavec/issues/114)
unassigned | P2 | [.github/workflows/ci.yml](../.github/workflows/ci.yml)
[pyproject.toml](../pyproject.toml)
[CONTRIBUTING.md](../CONTRIBUTING.md) | Add Python 3.13 to CI and run the suite plus dependency installation there. Update supported-version claims only after the run is green; retain coverage of existing supported versions. |
+| [#115 — Dependency vulnerability audit in CI](https://github.com/codeforstartups/dynavec/issues/115)
unassigned | P1 | [.github/workflows/ci.yml](../.github/workflows/ci.yml)
[pyproject.toml](../pyproject.toml) | Add a dependency vulnerability audit job with a documented triage/expiry process for exceptions. Define which base and optional extras it covers; demonstrate the job fails for a known vulnerable fixture or controlled check. |
+| [#125 — Dashboard: `dynavec dashboard` CLI to launch it locally](https://github.com/codeforstartups/dynavec/issues/125)
unassigned | P2 | [src/dynavec/cli.py](../src/dynavec/cli.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/next.config.mjs](../dashboard/next.config.mjs) | Add dynavec dashboard --port, documented telemetry wiring and static bundle serving/browser launch. Test startup, port conflicts and API integration; depend on the security model in #137. |
+| [#127 — Telemetry data model: DynamoDB events table](https://github.com/codeforstartups/dynavec/issues/127)
unassigned | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[src/dynavec/provisioning.py](../src/dynavec/provisioning.py) | TelemetryRecorder is an in-memory ring buffer. Add an opt-in DynamoDB event schema/store with TTL, bounded asynchronous writes, provisioning and privacy documentation. |
+| [#129 — Dashboard view: Resources (buckets, indexes, tables)](https://github.com/codeforstartups/dynavec/issues/129)
unassigned | P2 | [src/dynavec/client.py](../src/dynavec/client.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx) | Reuse describe/list helpers in a resources API and dashboard cards for buckets/indexes/tables. Add pagination, empty/error states and tests; there is no /api/resources route today. |
+| [#130 — Dashboard view: Namespace explorer](https://github.com/codeforstartups/dynavec/issues/130)
unassigned | P2 | [src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx) | Share one namespace-explorer implementation with #141. Add paginated counts/size summaries and detail/search UI, avoiding an unbounded full scan for each dashboard refresh. |
+| [#131 — Dashboard view: Cost panel](https://github.com/codeforstartups/dynavec/issues/131)
@tikhandesanket | P2 | [benchmarks/cost_model.py](../benchmarks/cost_model.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx) | Expose the existing cost model through a cost API using live size/volume inputs, then build a breakdown panel. Show assumptions, pricing date and an explicit estimate label. |
+| [#141 — Dashboard: Add Namespace Explorer](https://github.com/codeforstartups/dynavec/issues/141)
@osamashabih6960 | P2 | [dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py) | This overlaps #130. Agree on one owner/acceptance checklist and implement one namespace list, counts, filter and detail flow; link or consolidate the duplicate issue. |
+| [#286 — Prompt templates & output parsers](https://github.com/codeforstartups/dynavec/issues/286)
@tikhandesanket | P2 | [src/dynavec/chat](../src/dynavec/chat)
[src/dynavec/nodes/builtin.py](../src/dynavec/nodes/builtin.py) | LLMNode accepts messages, but a reusable template/output-parser module is absent. Add message templates and JSON/Pydantic parsers with clear validation errors and rendering/parser tests. |
+| [#291 — Visual builder: ReactFlow canvas to compose orchestrations](https://github.com/codeforstartups/dynavec/issues/291)
unassigned | P2 | [dashboard/package.json](../dashboard/package.json)
[dashboard/app/page.tsx](../dashboard/app/page.tsx)
[src/dynavec/nodes/registry.py](../src/dynavec/nodes/registry.py) | Choose dashboard versus standalone ownership, then implement a ReactFlow canvas using the node registry and #282 graph schema. Test create/connect/configure/save/reload and incompatible ports. |
+| [#292 — Code ⇄ visual round-trip (import/export graph as code)](https://github.com/codeforstartups/dynavec/issues/292)
unassigned | P2 | [src/dynavec/nodes/base.py](../src/dynavec/nodes/base.py)
[src/dynavec/agents/graph.py](../src/dynavec/agents/graph.py) | Define one versioned graph representation first (#282), then implement SDK/code-to-canvas import/export. Add semantic round-trip tests for nodes, edges, config and custom nodes; do not execute arbitrary source to import it. |
+| [#293 — Live run visualization on the canvas](https://github.com/codeforstartups/dynavec/issues/293)
unassigned | P2 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[dashboard/app/page.tsx](../dashboard/app/page.tsx) | After #291/#299, stream the same run/span events to canvas nodes. Test start/finish/error state, reconnects and inputs/outputs without creating a second telemetry format. |
+| [#294 — Debug & iterate: step through, re-run a single node, pin test inputs](https://github.com/codeforstartups/dynavec/issues/294)
unassigned | P3 | [src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py)
[dashboard/app/page.tsx](../dashboard/app/page.tsx) | Build on #281/#291: add step/pause, pinned inputs and single-node replay with inspectable edge state. Test reproducibility and preservation of the original run. |
+| [#297 — Durable execution on Step Functions / Workflows](https://github.com/codeforstartups/dynavec/issues/297)
unassigned | P2 | [src/dynavec/agents/graph.py](../src/dynavec/agents/graph.py)
[src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py) | Specify a durable execution backend around stable graph/state interfaces, then implement Step Functions/Workflows adapters with idempotent steps, retries and resume tests. DynamoDB checkpoints alone do not provide managed execution. |
+| [#298 — One-command serverless deploy (Lambda / Cloud Run), in-account](https://github.com/codeforstartups/dynavec/issues/298)
unassigned | P2 | [docs/DEPLOY.md](../docs/DEPLOY.md)
[src/dynavec/cli.py](../src/dynavec/cli.py) | The deployment document is not a one-command agent deployment flow. Add packaging and a deploy command with an explicit resource/IAM plan; verify provision/run/log/teardown in a dedicated cloud test environment. |
+| [#307 — Multi-cloud: pluggable GCP backends (Vertex, Firestore, Cloud Run, Workflows)](https://github.com/codeforstartups/dynavec/issues/307)
unassigned | P3 | [src/dynavec/config.py](../src/dynavec/config.py)
[src/dynavec/stores](../src/dynavec/stores)
[pyproject.toml](../pyproject.toml) | Stabilize provider-neutral store/model/execution interfaces, then implement GCP adapters and a parity matrix. Keep GCP dependencies optional and add contract tests plus a documented cloud smoke run. |
+| [#308 — Multi-cloud: pluggable Azure backends (Azure OpenAI, Cosmos DB, Functions, Durable Functions)](https://github.com/codeforstartups/dynavec/issues/308)
unassigned | P3 | [src/dynavec/config.py](../src/dynavec/config.py)
[src/dynavec/stores](../src/dynavec/stores)
[pyproject.toml](../pyproject.toml) | Reuse the backend contracts from #307, add Azure model/store/compute/durable/secrets adapters as optional extras, and verify an example graph against the published parity matrix. |
+| [#321 — feat(ingest): Support cursor pagination in MCPResourceSource](https://github.com/codeforstartups/dynavec/issues/321)
@neha-5456 | P2 | [src/dynavec/ingest.py](../src/dynavec/ingest.py)
[tests/test_ingest.py](../tests/test_ingest.py) | MCPResourceSource still calls list_resources once. Coordinate with the assignee to follow nextCursor/next_cursor in objects and mappings, forward cursor and test multiple/empty pages plus repeated-cursor protection. |
+
+### Epic (5)
+
+| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
+|---|---|---|---|
+| [#122 — [EPIC] Telemetry & resource dashboard](https://github.com/codeforstartups/dynavec/issues/122)
unassigned | P2 | [dashboard/README.md](../dashboard/README.md)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/app/page.tsx](../dashboard/app/page.tsx) | Track child completion individually: persistence #127, launch/security #125/#137, resources/namespaces #129/#130/#141, cost #131 and eval UI #136. Reconcile the original Vite plan with the shipped Next.js app. |
+| [#273 — [EPIC] dynaflow — serverless agent orchestration engine + visual builder](https://github.com/codeforstartups/dynavec/issues/273)
unassigned | P2 | [src/dynavec/agents/graph.py](../src/dynavec/agents/graph.py)
[src/dynavec/nodes/graph.py](../src/dynavec/nodes/graph.py)
[src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py)
[docs/roadmap.md](../docs/roadmap.md) | Graph runtimes, nodes, agents and checkpointing have landed. Track remaining schema/round-trip, snapshots, visual builder and deployment children; update the stale roadmap claim that the engine is only pseudocode. |
+| [#274 — [EPIC] dynalogs — in-account observability for agent runs](https://github.com/codeforstartups/dynavec/issues/274)
unassigned | P2 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py) | Structured run/node logs and query telemetry exist. Complete parented spans, durable run history, metrics and dashboard integration under #299/#301/#302 rather than recreating logging. |
+| [#275 — [EPIC] dynaevals — evaluation, datasets & CI gating](https://github.com/codeforstartups/dynavec/issues/275)
unassigned | P2 | [src/dynavec/eval](../src/dynavec/eval)
[PR #351 open](https://github.com/codeforstartups/dynavec/pull/351) | Retrieval/RAG metrics and trend tracking exist. Finish dataset round-trip #304, review CI gate #305 and attach evals to runs #306; keep the epic open until those flows are verified. |
+| [#276 — [EPIC] Ecosystem launch — landing page & cross-library story (dynavec / dynaflow / dynalogs / dynaevals)](https://github.com/codeforstartups/dynavec/issues/276)
unassigned | P3 | [opensource/dynavec/index.html](../opensource/dynavec/index.html)
[tools/build_docs.py](../tools/build_docs.py)
[docs/roadmap.md](../docs/roadmap.md) | Ecosystem pages and landing-page sections exist after #347. Review #310/#311, then reconcile capability claims with shipped runtime/visual/deployment status before closing the ecosystem epic. |
+
+## Validation and limits
+
+- Python 3.12.15, installed via `uv venv --python 3.12` and the repository's `make install` target.
+- Focused query/store/error tests pass, including real SDK stubs; lint passes for the changed tests.
+- `make run-ci` passed: ruff, strict mypy (87 checked files), **1,125 tests passed, 2 opt-in live AWS tests skipped**, and 1 snapshot passed. The focused store/error/logging selection passed all 100 tests; changed-test lint and `git diff --check` also passed.
+- No live AWS resources, credentialed model calls, external Redis/Memcached clusters, browser layout acceptance, or deployed orchestration were exercised. Missing/partial assessments concern inspected deliverables and explicitly identified acceptance gaps.
+- This is a dated snapshot. Recheck current issue assignments, PR state and source before starting follow-up work. This PR does not close the entire backlog or automatically change issue ownership.
From d8a1c8b2e4efb99b02905e78aa5364bc66cec80b Mon Sep 17 00:00:00 2001
From: Lawliet2004 <168631200+Lawliet2004@users.noreply.github.com>
Date: Sun, 8 Nov 2026 13:00:43 +0530
Subject: [PATCH 3/3] docs: remove issue audit from pagination PR
---
docs/ISSUE_AUDIT_2026-11-06.md | 165 ---------------------------------
1 file changed, 165 deletions(-)
delete mode 100644 docs/ISSUE_AUDIT_2026-11-06.md
diff --git a/docs/ISSUE_AUDIT_2026-11-06.md b/docs/ISSUE_AUDIT_2026-11-06.md
deleted file mode 100644
index d541a9c..0000000
--- a/docs/ISSUE_AUDIT_2026-11-06.md
+++ /dev/null
@@ -1,165 +0,0 @@
-# Issue audit — 6 November 2026
-
-## Scope and evidence
-
-Audit baseline: `development` at `b9c90d7`. GitHub inventory was read on 6 November 2026: **192 issues, 79 open and 113 closed; 158 pull requests, 7 open** before this audit PR. Every open issue appears exactly once in the inventory below. Closed issues were sampled against relevant code and PRs; this is not a reproduction of every closed issue.
-
-Assessments compare issue acceptance criteria, current source/tests, and merged/open PR metadata. A linked or closed PR is not proof that a feature shipped. In particular, the comment on #29 links provisioning-test PR #315, not an edge-storage implementation. Static code presence is not an end-to-end production guarantee.
-
-[Issue tracker](https://github.com/codeforstartups/dynavec/issues) · [Pull requests](https://github.com/codeforstartups/dynavec/pulls) · [Contribution/assignment policy](../CONTRIBUTING.md#finding--claiming-issues). Ownership is the snapshot assignee shown on each row; “unassigned” is not an assignment. Coordinate existing work before claiming an issue. This PR fixes a regression discovered during the audit rather than implementing another contributor's assigned feature.
-
-| Assessment | Open issues | Meaning |
-|---|---:|---|
-| Review closure | 11 | Implementation evidence exists; maintainer acceptance or a narrow follow-up is still required. |
-| Partial | 28 | Some requested behavior exists, with explicit gaps listed below. |
-| Open PR | 4 | Implementation is being proposed; review/merge and acceptance checks remain. |
-| Missing | 31 | The requested public flow or essential deliverable was not found in the inspected code. |
-| Epic | 5 | Parent tracking issue; resolve its children rather than treating it as one patch. |
-
-Priorities are audit recommendations: **P1** correctness/security or a blocking integration; **P2** functional completion; **P3** documentation, polish or later architecture. They are not repository labels.
-
-## Recommended order
-
-| Order | Work | Completion check |
-|---|---|---|
-| 1 | Merge the QueryVectors fix in this PR. | Real boto3/aioboto3 stubs work without a query paginator; all pages are consumed using `nextToken`; top-k, buffering, errors and rate limiting remain correct. |
-| 2 | Coordinate correctness/security gaps #8, #28, #97, #100, #137 and #342 with their owners. | Cross-model caches are isolated; partial writes have a repair strategy; sample IAM is scoped; formatted secrets are redacted; dashboard APIs enforce their access model; routing is invoked once. |
-| 3 | Review pending PRs: #348 → #349, #351, and draft #316. | Verify acceptance cases before merging; retire superseded #332/#336 in coordination with their authors. |
-| 4 | Review the closure candidates below and reconcile #130/#141. | Update issue state only after maintainer acceptance; retain specific remaining gaps as follow-ups. |
-| 5 | Finish persistent telemetry/run history and the shared graph schema. | #127/#128/#299/#301/#302 and #282/#284 provide stable data contracts before building dependent UI. |
-| 6 | Build visual/eval/deployment flows on those contracts. | Dataset/gate/run links, canvas round-trip and deployment each have an executable acceptance scenario; expand to GCP/Azure afterward. |
-
-## Confirmed defects found while auditing
-
-### Selected fix: S3 Vectors queries stop early or cannot start
-
-Both stores requested a `query_vectors` paginator. The environment produced by `make install` resolves boto3/botocore **1.40.61**, where `client.can_paginate("query_vectors")` is false and `get_paginator` raises `OperationNotPageableError`. Newer AWS APIs support continuation, but both stores checked `NextToken` instead of the documented **`nextToken`**. This silently discarded later results. Earlier tests used the same incorrect uppercase key.
-
-The fix calls `query_vectors` directly in sync and async stores and forwards a nonempty `nextToken`. This supports a single response on older SDKs and continuation on SDK/service versions exposing it, without changing dependency constraints. A caller still needs an SDK supporting any request fields/features it uses; this change does not add pagination support to an old service model.
-
-Regression coverage includes real SDK clients with Stubber/AioStubber, multiple service pages, empty intermediate pages, client chunk buffering (including a page size larger than top-k), cursor forwarding, top-k limits, one rate-limit acquisition per request, permanent error translation and retry restart. Tests with realistic lowercase tokens reproduced 15 failures before the production fix. The historical pagination issue [#16](https://github.com/codeforstartups/dynavec/issues/16) is closed, so its tracker state alone did not establish correctness.
-
-Sources: [AWS QueryVectors request/response](https://docs.aws.amazon.com/AmazonS3/latest/API/API_S3VectorBuckets_QueryVectors.html), [boto3 query_vectors](https://docs.aws.amazon.com/boto3/latest/reference/services/s3vectors/client/query_vectors.html), and the installed SDK service/paginator model inspected offline.
-
-### Follow-ups reproduced without external services
-
-- **#8:** two CachedEmbedder instances of the same backend class, with different model identities and a shared cache, reuse the first model's vector under the default class-based key. Use explicit distinct `model_key` values as the current workaround; a full fix must encode actual embedding identity.
-- **#342:** one Supervisor.run call invokes a configured router **twice**, as shown by a counting router. A stateful router can produce different selections between validation/execution. Reuse the first selected route and add an invocation-count regression.
-
-These findings are resolution work for the relevant issues, not claims that those issues are fixed by this PR. Security-related reproduction details are excluded from this public report in accordance with [SECURITY.md](../SECURITY.md).
-
-## Open-issue inventory
-
-Each row states source evidence and a concrete next action. “Review closure” is a recommendation for maintainer review, not an automatic closure request. UI acceptance and cloud deployment checks remain separate from offline Python tests.
-
-### Review closure (11)
-
-| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
-|---|---|---|---|
-| [#7 — Retry/backoff for OpenAI embedder rate limits](https://github.com/codeforstartups/dynavec/issues/7)
@tikhandesanket | P2 | [src/dynavec/embeddings/openai.py](../src/dynavec/embeddings/openai.py)
[tests/test_openai_embeddings.py](../tests/test_openai_embeddings.py)
[PR #242 merged](https://github.com/codeforstartups/dynavec/pull/242) | 429/5xx retries and numeric Retry-After handling exist. Compare the tested header formats with the intended acceptance criteria, then close or narrow the issue. |
-| [#39 — TTL jitter for DynamoDBCache](https://github.com/codeforstartups/dynavec/issues/39)
@be-student | P3 | [src/dynavec/cache.py](../src/dynavec/cache.py)
[tests/test_cache.py](../tests/test_cache.py)
[tools/build_docs.py](../tools/build_docs.py) | DynamoDBCache has bounded configurable TTL jitter, validation, deterministic tests and a caching guide. Review the existing behavior and close; a closed-unmerged PR alone would not establish completion. |
-| [#43 — Cache warmup utility](https://github.com/codeforstartups/dynavec/issues/43)
@arshsk16 | P3 | [src/dynavec/cache.py](../src/dynavec/cache.py)
[tests/test_cache.py](../tests/test_cache.py)
[CHANGELOG.md](../CHANGELOG.md) | warm_cache(client, queries) already populates a configured cache and has tests. Confirm the accepted public name relative to warm(queries), finish any dedicated usage docs, then close or narrow. |
-| [#69 — Semantic Kernel memory connector](https://github.com/codeforstartups/dynavec/issues/69)
@tikhandesanket | P2 | [src/dynavec/integrations/semantic_kernel.py](../src/dynavec/integrations/semantic_kernel.py)
[tests/test_semantic_kernel.py](../tests/test_semantic_kernel.py)
[examples/semantic_kernel_vector_store.py](../examples/semantic_kernel_vector_store.py) | A Semantic Kernel vector-store adapter, example and tests exist. Confirm that this fulfills the intended memory connector API on supported Semantic Kernel versions; close or specify the remaining interface gap. |
-| [#75 — Optimized Product Quantization (OPQ)](https://github.com/codeforstartups/dynavec/issues/75)
@tikhandesanket | P2 | [src/dynavec/quantization.py](../src/dynavec/quantization.py)
[tests/test_quantization.py](../tests/test_quantization.py)
[benchmarks/benchmark_opq.py](../benchmarks/benchmark_opq.py) | OPQRotation and OptimizedProductQuantizer, tests and a recall benchmark exist. Review benchmark methodology/results against the acceptance criteria and close or narrow; PR #198 being closed is not sufficient evidence on its own. |
-| [#79 — Hot/cold sync on writes](https://github.com/codeforstartups/dynavec/issues/79)
@Isha-Zaka | P2 | [src/dynavec/client.py](../src/dynavec/client.py)
[tests/test_hot_tier.py](../tests/test_hot_tier.py) | The sync client fans successful writes/deletes into the hot tier, with write-through and delete consistency tests. Verify the intended scope, then close; failure recovery across stores remains separate work under #28. |
-| [#103 — Cookbook: production RAG with namespaces](https://github.com/codeforstartups/dynavec/issues/103)
@tikhandesanket | P3 | [examples/multi_tenant_rag.py](../examples/multi_tenant_rag.py)
[tools/build_docs.py](../tools/build_docs.py)
[PR #239 merged](https://github.com/codeforstartups/dynavec/pull/239) | The multi-tenant RAG example and namespaces documentation exist. Review the recipe and tenant-authentication guidance against production expectations, then close or narrow. |
-| [#138 — Dashboard: dark mode + theme parity with the landing page](https://github.com/codeforstartups/dynavec/issues/138)
@tikhandesanket | P3 | [dashboard/components/ThemeToggle.tsx](../dashboard/components/ThemeToggle.tsx)
[dashboard/app/layout.tsx](../dashboard/app/layout.tsx)
[dashboard/app/globals.css](../dashboard/app/globals.css)
[PR #187 merged](https://github.com/codeforstartups/dynavec/pull/187) | Dark palette and persisted theme toggle are implemented. Capture the requested parity screenshots and obtain design acceptance before closing; no new theme implementation is needed. |
-| [#176 — dynavec complete landing page revamp](https://github.com/codeforstartups/dynavec/issues/176)
@tikhandesanket | P3 | [opensource/dynavec/index.html](../opensource/dynavec/index.html)
[opensource/dynavec/styles.css](../opensource/dynavec/styles.css)
[PR #347 merged](https://github.com/codeforstartups/dynavec/pull/347) | The latest merged PR redesigns the landing page and docs. Request visual acceptance of that implementation and close or list concrete remaining defects; avoid starting another full redesign. |
-| [#310 — Landing page: "How they fit together" diagram](https://github.com/codeforstartups/dynavec/issues/310)
unassigned | P3 | [opensource/dynavec/index.html](../opensource/dynavec/index.html)
[opensource/dynavec/styles.css](../opensource/dynavec/styles.css)
[PR #347 merged](https://github.com/codeforstartups/dynavec/pull/347) | The landing page now includes a four-stage ecosystem cycle and SVG artwork. Verify the single flow/caption and desktop/mobile readability against the issue, then close or specify the visual gap. |
-| [#311 — Landing page: positioning vs LangChain / LangGraph (parity + differentiators)](https://github.com/codeforstartups/dynavec/issues/311)
unassigned | P3 | [opensource/dynavec/index.html](../opensource/dynavec/index.html)
[docs/roadmap.md](../docs/roadmap.md)
[PR #347 merged](https://github.com/codeforstartups/dynavec/pull/347) | The landing page includes a capability comparison with availability markers. Cross-check every status against the actual SDK/visual/deployment flows and approve the wording before closure. |
-
-### Open PR (4)
-
-| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
-|---|---|---|---|
-| [#40 — Redis Cluster / TLS support](https://github.com/codeforstartups/dynavec/issues/40)
@sriram1900 | P2 | [src/dynavec/cache.py](../src/dynavec/cache.py)
[PR #316 open](https://github.com/codeforstartups/dynavec/pull/316) | PR #316 describes itself as work in progress. Coordinate with its author to add cluster-client selection, TLS options and docs, then run backend parity tests. Keep the issue open until implementation is merged. |
-| [#290 — Conversation / short-term memory store on DynamoDB](https://github.com/codeforstartups/dynavec/issues/290)
@shivamm-gupta | P2 | [PR #349 open](https://github.com/codeforstartups/dynavec/pull/349)
[PR #336 open](https://github.com/codeforstartups/dynavec/pull/336)
[src/dynavec/agents/react.py](../src/dynavec/agents/react.py) | Review #348 first, then the maintainer port #349 stacked on it. The #349 description supersedes #336; avoid another implementation and verify multi-turn memory, trimming, restart and interrupt/resume together. |
-| [#296 — Human-in-the-loop / interrupts](https://github.com/codeforstartups/dynavec/issues/296)
@Uzmaa7 | P1 | [PR #348 open](https://github.com/codeforstartups/dynavec/pull/348)
[PR #332 open](https://github.com/codeforstartups/dynavec/pull/332)
[src/dynavec/agents/react.py](../src/dynavec/agents/react.py) | Review maintainer port #348, which supersedes #332. Verify approve/reject, provider-valid tool message ordering and restart-safe resume; separately verify the issue's timeout/expiry criterion before closure. |
-| [#305 — Regression runs & CI gating (fail when a metric drops)](https://github.com/codeforstartups/dynavec/issues/305)
unassigned | P1 | [src/dynavec/eval/trend.py](../src/dynavec/eval/trend.py)
[PR #351 open](https://github.com/codeforstartups/dynavec/pull/351) | Review #351 instead of rebuilding its gate. Verify threshold polarity, zero/NaN/missing metric handling, persisted baselines, CLI exit codes and the GitHub Actions example; the unmerged PR does not resolve the issue. |
-
-### Partial (28)
-
-| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
-|---|---|---|---|
-| [#8 — Optional embedding cache to avoid re-embedding identical text](https://github.com/codeforstartups/dynavec/issues/8)
@tikhandesanket | P1 | [src/dynavec/embeddings/cached.py](../src/dynavec/embeddings/cached.py)
[src/dynavec/embeddings/cache.py](../src/dynavec/embeddings/cache.py)
[tests/test_embedding_cache.py](../tests/test_embedding_cache.py) | Caching exists, but the default model key is the embedder class name. Include provider/model/dimension identity or require an explicit key when sharing a cache; test two models of the same class sharing one backend. |
-| [#26 — Support provisioned billing + autoscaling docs](https://github.com/codeforstartups/dynavec/issues/26)
unassigned | P3 | [src/dynavec/provisioning.py](../src/dynavec/provisioning.py)
[src/dynavec/config.py](../src/dynavec/config.py)
[ARCHITECTURE.md](../ARCHITECTURE.md) | PROVISIONED mode already creates 5 RCU/5 WCU. Document capacity planning, application autoscaling setup/IAM and an example config; distinguish the current fixed defaults from automatic scaling. |
-| [#51 — Client-side rate limiting / backpressure](https://github.com/codeforstartups/dynavec/issues/51)
@GenZ-CODER-X | P2 | [src/dynavec/utils.py](../src/dynavec/utils.py)
[src/dynavec/config.py](../src/dynavec/config.py)
[src/dynavec/stores/s3vectors.py](../src/dynavec/stores/s3vectors.py)
[tests/test_s3vectors.py](../tests/test_s3vectors.py)
[tests/test_async_s3vectors.py](../tests/test_async_s3vectors.py) | TokenBucket, put_rps/query_rps and sync/async hooks already exist. Add deterministic burst/refill and concurrent contention tests, document limits, and audit remaining put/query paths before closure. |
-| [#62 — Incremental / delta ingestion](https://github.com/codeforstartups/dynavec/issues/62)
@Pratham1603 | P2 | [src/dynavec/ingest.py](../src/dynavec/ingest.py)
[src/dynavec/metadata.py](../src/dynavec/metadata.py) | Within-call deduplication exists, but there is no stored-hash comparison across runs. Add an opt-in per-chunk skip_unchanged path, preserve unconditional overwrite by default, and test changed metadata/text plus records without hashes. |
-| [#64 — Ingest MCP tools & prompts primitives](https://github.com/codeforstartups/dynavec/issues/64)
@GenZ-CODER-X | P2 | [src/dynavec/ingest.py](../src/dynavec/ingest.py)
[src/dynavec/agents/connectors.py](../src/dynavec/agents/connectors.py) | MCPResourceSource ingests resources and agent connectors expose tools, but ingestion adapters for tool/prompt definitions are absent. Add duck-typed source adapters and fake-session tests without executing tools. |
-| [#85 — dynavec CLI: provision/upsert/query/stats](https://github.com/codeforstartups/dynavec/issues/85)
unassigned | P2 | [src/dynavec/cli.py](../src/dynavec/cli.py)
[tests/test_cli.py](../tests/test_cli.py)
[pyproject.toml](../pyproject.toml) | The CLI already has version/doctor/export/import/mcp. Add provision/upsert/query/stats commands, explicit config/.env precedence and offline parser/error tests. |
-| [#89 — moto-based DynamoDB integration tests](https://github.com/codeforstartups/dynavec/issues/89)
unassigned | P2 | [tests/test_provisioning.py](../tests/test_provisioning.py)
[tests/test_dynamodb.py](../tests/test_dynamodb.py)
[tests/test_dynamodb_ttl.py](../tests/test_dynamodb_ttl.py)
[tests/test_async_dynamodb_moto.py](../tests/test_async_dynamodb_moto.py)
[.github/workflows/ci.yml](../.github/workflows/ci.yml) | Moto provisioning/versioning/TTL and async round-trip tests already run through pytest in CI. Complete explicit batch put/get/delete round-trips and failure cases, then close or narrow instead of adding a duplicate suite. |
-| [#93 — Credentials assume-role test](https://github.com/codeforstartups/dynavec/issues/93)
unassigned | P2 | [src/dynavec/credentials.py](../src/dynavec/credentials.py)
[tests/test_credentials.py](../tests/test_credentials.py) | The async test mocks AWSCredentials.session(), bypassing STS. Stub STS assume_role directly and assert role/session/external ID forwarding and temporary credentials, including failures and source-profile region behavior. |
-| [#95 — Release automation notes / changelog](https://github.com/codeforstartups/dynavec/issues/95)
unassigned | P3 | [CHANGELOG.md](../CHANGELOG.md)
[.github/workflows/publish.yml](../.github/workflows/publish.yml)
[tools/build_docs.py](../tools/build_docs.py) | Changelog, release publishing workflow and release notes already exist. Add a maintainer release checklist covering version/tag consistency, build checks and verification of the published package. |
-| [#96 — Coverage + PyPI + CI badges in README](https://github.com/codeforstartups/dynavec/issues/96)
unassigned | P3 | [README.md](../README.md)
[opensource/dynavec/index.html](../opensource/dynavec/index.html)
[.github/workflows/ci.yml](../.github/workflows/ci.yml) | README PyPI/CI badges exist; coverage publishing and a coverage badge are absent. Define the coverage source/threshold, publish its report and verify all requested badge links on both surfaces. |
-| [#99 — KMS encryption docs for table + bucket](https://github.com/codeforstartups/dynavec/issues/99)
unassigned | P2 | [ARCHITECTURE.md](../ARCHITECTURE.md)
[docs/iam-policy.json](../docs/iam-policy.json)
[src/dynavec/provisioning.py](../src/dynavec/provisioning.py) | Encryption has a brief architecture note, but no SSE-KMS recipe or key-policy guidance. Document table/bucket creation settings, required KMS/IAM permissions and an example; do not imply provisioning currently accepts KMS configuration. |
-| [#100 — Redact secrets from any logging/errors](https://github.com/codeforstartups/dynavec/issues/100)
@tenten48tenten | P1 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[tests/test_logging.py](../tests/test_logging.py)
[tests/test_run_logging.py](../tests/test_run_logging.py) | Redaction helpers and tests exist. Complete an audit and synthetic-secret coverage across messages, tracebacks and AWS/provider errors; coordinate remediation with the assignee and follow SECURITY.md for any vulnerability details. |
-| [#101 — Auto-generate API reference (pdoc)](https://github.com/codeforstartups/dynavec/issues/101)
@raunak3208 | P3 | [tools/build_docs.py](../tools/build_docs.py)
[.github/workflows/pages.yml](../.github/workflows/pages.yml) | The static docs are hand-authored in build_docs.py. Add a reproducible pdoc API-reference build/publish step, optional dependency handling and a public-symbol smoke check. |
-| [#128 — Client telemetry hook (opt-in) to record query events](https://github.com/codeforstartups/dynavec/issues/128)
unassigned | P2 | [src/dynavec/client.py](../src/dynavec/client.py)
[src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[tests/test_telemetry.py](../tests/test_telemetry.py) | Search telemetry is opt-in via a recorder and omits query text by default. Add stage timings/result IDs as approved by the privacy contract, and a nonblocking persistent sink tied to #127; test enabled/disabled behavior. |
-| [#132 — Dashboard view: Query analytics (latency, QPS, cache hit-rate)](https://github.com/codeforstartups/dynavec/issues/132)
@raunak3208 | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[dashboard/components/Kpis.tsx](../dashboard/components/Kpis.tsx)
[dashboard/components/LatencyChart.tsx](../dashboard/components/LatencyChart.tsx)
[dashboard/components/VolumeChart.tsx](../dashboard/components/VolumeChart.tsx) | KPIs, percentiles, cache hit-rate and a range selector already exist. Add the requested latency percentile time-series and verify real-data behavior; coordinate with the current assignee. |
-| [#133 — Dashboard view: Per-query drill-down](https://github.com/codeforstartups/dynavec/issues/133)
unassigned | P2 | [dashboard/components/TraceDrawer.tsx](../dashboard/components/TraceDrawer.tsx)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[src/dynavec/client.py](../src/dynavec/client.py) | The query drawer and trace endpoint exist, but the drawer lacks stage waterfalls and result links/IDs. Persist stage timings and approved result details, then test drill-down with text capture both enabled and disabled. |
-| [#136 — Dashboard view: Retrieval-quality trends over time](https://github.com/codeforstartups/dynavec/issues/136)
@tikhandesanket | P2 | [src/dynavec/eval/trend.py](../src/dynavec/eval/trend.py)
[src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx) | EvalRunStore trends/comparison and eval API endpoints exist. Add the dashboard score/trend panel with dataset/model filters and run comparison; backend support alone does not finish the UI issue. |
-| [#137 — Dashboard: security — local-only, server-side creds, API token](https://github.com/codeforstartups/dynavec/issues/137)
unassigned | P1 | [src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/lib/api.ts](../dashboard/lib/api.ts) | The server binds to 127.0.0.1 by default, but APIs have no token check and serve() allows another host. Add a server-side local API authentication/origin model, document it and test unauthorized requests and credential exclusion. |
-| [#281 — Snapshots for resume, time-travel & replay](https://github.com/codeforstartups/dynavec/issues/281)
unassigned | P2 | [src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py)
[src/dynavec/nodes/graph.py](../src/dynavec/nodes/graph.py)
[tests/test_checkpointer.py](../tests/test_checkpointer.py) | Checkpoint history and failed-run resume exist. Add explicit immutable snapshot/fork/replay APIs with input overrides and tests proving historical snapshots stay unchanged. |
-| [#282 — Typed node system / node schema (ReactFlow-compatible)](https://github.com/codeforstartups/dynavec/issues/282)
unassigned | P2 | [src/dynavec/nodes/base.py](../src/dynavec/nodes/base.py)
[src/dynavec/nodes/graph.py](../src/dynavec/nodes/graph.py)
[src/dynavec/nodes/registry.py](../src/dynavec/nodes/registry.py) | Typed port declarations, dictionaries and a canvas palette exist. Add a versioned graph JSON loader/round-trip and validate port compatibility across edges; declarations alone do not enforce types. |
-| [#284 — Custom nodes via the SDK](https://github.com/codeforstartups/dynavec/issues/284)
unassigned | P2 | [src/dynavec/nodes/base.py](../src/dynavec/nodes/base.py)
[src/dynavec/nodes/registry.py](../src/dynavec/nodes/registry.py)
[tests/test_nodes.py](../tests/test_nodes.py) | Node subclassing/registration is already possible. Document a public custom-node example, execute it in CI and verify graph serialization/restoration through the shared schema from #282. |
-| [#295 — SDK & CLI: run / deploy / inspect with code⇄visual parity](https://github.com/codeforstartups/dynavec/issues/295)
unassigned | P2 | [src/dynavec/agents/graph.py](../src/dynavec/agents/graph.py)
[src/dynavec/cli.py](../src/dynavec/cli.py) | SDK graph execution exists; orchestration run/inspect/deploy CLI and code/canvas parity do not. Land the shared schema first, then add offline-tested commands; deployment also depends on #298. |
-| [#299 — Per-node traces/spans assembled into a run tree](https://github.com/codeforstartups/dynavec/issues/299)
@Isha-Zaka | P2 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[tests/test_run_logging.py](../tests/test_run_logging.py) | Run/node/span correlation IDs exist, but a parented run tree is missing. Add parent_span_id, start/end timing and stable span events, then test nested/parallel propagation and tree assembly. |
-| [#301 — Run history: searchable / filterable list with drill-down to a trace](https://github.com/codeforstartups/dynavec/issues/301)
unassigned | P2 | [src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py)
[src/dynavec/logging.py](../src/dynavec/logging.py)
[dashboard/components/TracesTable.tsx](../dashboard/components/TracesTable.tsx) | The UI lists query events; checkpoints/log sinks hold pieces of agent runs. Add a durable, indexed run-list API plus run detail/tree UI with status/time/model/tag filters and pagination. |
-| [#302 — Metrics: latency percentiles, QPS, cache hit-rate, error rate, token/cost per run](https://github.com/codeforstartups/dynavec/issues/302)
unassigned | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[dashboard/components/Kpis.tsx](../dashboard/components/Kpis.tsx)
[src/dynavec/logging.py](../src/dynavec/logging.py) | Query latency, traffic, cache and error aggregates exist. Add per-run model token/cost accounting, parented run attribution and consistent aggregates; test real usage values and unknown-price behavior. |
-| [#304 — Test cases & datasets (labeled inputs → expected outputs/relevance)](https://github.com/codeforstartups/dynavec/issues/304)
unassigned | P2 | [src/dynavec/eval/retrieval.py](../src/dynavec/eval/retrieval.py)
[src/dynavec/eval/runner.py](../src/dynavec/eval/runner.py)
[tests/test_retrieval_eval.py](../tests/test_retrieval_eval.py) | LabeledQuery, JSON/JSONL loading and per-example/aggregate retrieval scores exist. Add dataset saving/round-trip, tags/expected-output schema and graph-suite support without duplicating the retrieval runner. |
-| [#306 — In-loop eval: attach evals to the same run/trace view](https://github.com/codeforstartups/dynavec/issues/306)
unassigned | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[src/dynavec/eval/trend.py](../src/dynavec/eval/trend.py) | Eval metric fields and endpoints exist, but no complete run/span-tree association and score drill-down UI. Join eval results to execution run IDs and test query-by-run plus dashboard visibility. |
-| [#342 — feat(dynaflow): introduce multi-agent supervisor and handoff primitives](https://github.com/codeforstartups/dynavec/issues/342)
@tikhandesanket | P1 | [src/dynavec/agents/supervisor.py](../src/dynavec/agents/supervisor.py)
[src/dynavec/agents/handoff.py](../src/dynavec/agents/handoff.py)
[src/dynavec/agents/state.py](../src/dynavec/agents/state.py)
[tests/test_supervisor_routing.py](../tests/test_supervisor_routing.py)
[PR #345 merged](https://github.com/codeforstartups/dynavec/pull/345) | Supervisor/state/handoff implementations exist under dynavec. Supervisor.run currently calls its router twice per run: call once and test a stateful router. Separately agree whether the dynaflow package-boundary requirement remains before closure. |
-
-### Missing (31)
-
-| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
-|---|---|---|---|
-| [#17 — Add delete-by-filter helper](https://github.com/codeforstartups/dynavec/issues/17)
unassigned | P2 | [src/dynavec/client.py](../src/dynavec/client.py)
[src/dynavec/namespace.py](../src/dynavec/namespace.py)
[tests/test_list_vectors.py](../tests/test_list_vectors.py) | Define supported filter semantics, enumerate the complete namespace, evaluate matching metadata, and batch delete both stores with cache/hot-tier invalidation. Test more than one batch, empty matches, failures and tenant isolation; ANN top-k is not exhaustive deletion. |
-| [#21 — Optional GSI for metadata access patterns](https://github.com/codeforstartups/dynavec/issues/21)
unassigned | P2 | [src/dynavec/config.py](../src/dynavec/config.py)
[src/dynavec/provisioning.py](../src/dynavec/provisioning.py) | Design explicit metadata access patterns and projected/indexed fields; add opt-in GSI provisioning and a query helper. Cover existing-table migration, pagination and the no-GSI default. |
-| [#28 — Transactional write across both stores (best-effort)](https://github.com/codeforstartups/dynavec/issues/28)
unassigned | P1 | [src/dynavec/client.py](../src/dynavec/client.py)
[src/dynavec/async_client.py](../src/dynavec/async_client.py) | Document the two-store failure model and implement a bounded repair/journal strategy for partial writes. Test failure of either store and retries over existing records; blind deletion as rollback can destroy an earlier version. |
-| [#29 — Sort-key adjacency design for high fan-out nodes](https://github.com/codeforstartups/dynavec/issues/29)
@tikhandesanket | P2 | [src/dynavec/graph.py](../src/dynavec/graph.py)
[PR #315 merged](https://github.com/codeforstartups/dynavec/pull/315) | Adjacency still lives in embedded lists. Propose separate edge items and a migration/backfill path, then benchmark high fan-out and pagination. PR #315 tests table provisioning; it does not implement this issue despite the issue comment linking it. |
-| [#42 — Memcached backend](https://github.com/codeforstartups/dynavec/issues/42)
@GenZ-CODER-X | P3 | [src/dynavec/cache.py](../src/dynavec/cache.py)
[pyproject.toml](../pyproject.toml) | Add an optional Memcached client extra and BaseCache implementation, including namespace invalidation and TTL/stats parity tests. |
-| [#45 — Cohere Rerank API integration](https://github.com/codeforstartups/dynavec/issues/45)
@troublerone | P2 | [src/dynavec/retrieval.py](../src/dynavec/retrieval.py)
[src/dynavec/client.py](../src/dynavec/client.py)
[pyproject.toml](../pyproject.toml) | Add a pluggable hosted reranker with caller-supplied Cohere credentials and an optional extra. Test ordered result mapping, empty inputs and provider errors; existing cross-encoder reranking is a different backend. |
-| [#52 — Thread-safety tests for stores](https://github.com/codeforstartups/dynavec/issues/52)
unassigned | P2 | [tests/test_client_inmemory.py](../tests/test_client_inmemory.py)
[tests/test_connection_pool.py](../tests/test_connection_pool.py) | Add barrier-driven concurrent upsert/search scenarios against the fakes, asserting complete payloads and namespace isolation. Separate fake-store thread safety from guarantees of the AWS-backed client. |
-| [#53 — Process pool for CPU-bound PQ training](https://github.com/codeforstartups/dynavec/issues/53)
@tikhandesanket | P3 | [src/dynavec/quantization.py](../src/dynavec/quantization.py)
[benchmarks/benchmark_quantization.py](../benchmarks/benchmark_quantization.py) | Add opt-in process-based PQ fitting only after benchmarking serialization/startup costs. Keep deterministic seeds, a sequential default and a benchmark demonstrating where the process pool helps. |
-| [#82 — OpenTelemetry tracing](https://github.com/codeforstartups/dynavec/issues/82)
unassigned | P2 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[src/dynavec/client.py](../src/dynavec/client.py)
[pyproject.toml](../pyproject.toml) | Add optional OpenTelemetry spans for embed/query/hydrate/rerank, preserve caller trace context, and supply a collector example. Test span parentage and operation without the extra installed. |
-| [#83 — CloudWatch metrics emission](https://github.com/codeforstartups/dynavec/issues/83)
unassigned | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[src/dynavec/config.py](../src/dynavec/config.py)
[docs/iam-policy.json](../docs/iam-policy.json) | Add an opt-in CloudWatch emitter with batching, bounded buffers and documented IAM. Test dimension/cardinality limits and ensure emitter failure does not fail retrieval. |
-| [#88 — CLI: migrate from Pinecone](https://github.com/codeforstartups/dynavec/issues/88)
@Isha-Zaka | P2 | [src/dynavec/cli.py](../src/dynavec/cli.py)
[examples/pinecone_vs_dynavec.py](../examples/pinecone_vs_dynavec.py) | Add a resumable, batched Pinecone migration command preserving IDs, namespaces, metadata and vector dimensions. Test source pagination and interrupted migration; comparison examples are not an importer. |
-| [#97 — Tighten S3 Vectors resource ARNs in the sample policy](https://github.com/codeforstartups/dynavec/issues/97)
unassigned | P1 | [README.md](../README.md)
[docs/iam-policy.json](../docs/iam-policy.json)
[ARCHITECTURE.md](../ARCHITECTURE.md) | Separate account-level list/create permissions from bucket/index data permissions using the current AWS authorization resource table. Scope data actions to the configured bucket/index and test documented runtime/provisioning policies. |
-| [#102 — Migrating from Pinecone guide](https://github.com/codeforstartups/dynavec/issues/102)
unassigned | P3 | [examples/pinecone_vs_dynavec.py](../examples/pinecone_vs_dynavec.py)
[tools/build_docs.py](../tools/build_docs.py) | Write a migration guide with a concepts table, namespace/metric/filter differences, batched export/import examples and a verification checklist. Keep documentation separate from the importer in #88. |
-| [#114 — Add Python 3.13 to the CI matrix](https://github.com/codeforstartups/dynavec/issues/114)
unassigned | P2 | [.github/workflows/ci.yml](../.github/workflows/ci.yml)
[pyproject.toml](../pyproject.toml)
[CONTRIBUTING.md](../CONTRIBUTING.md) | Add Python 3.13 to CI and run the suite plus dependency installation there. Update supported-version claims only after the run is green; retain coverage of existing supported versions. |
-| [#115 — Dependency vulnerability audit in CI](https://github.com/codeforstartups/dynavec/issues/115)
unassigned | P1 | [.github/workflows/ci.yml](../.github/workflows/ci.yml)
[pyproject.toml](../pyproject.toml) | Add a dependency vulnerability audit job with a documented triage/expiry process for exceptions. Define which base and optional extras it covers; demonstrate the job fails for a known vulnerable fixture or controlled check. |
-| [#125 — Dashboard: `dynavec dashboard` CLI to launch it locally](https://github.com/codeforstartups/dynavec/issues/125)
unassigned | P2 | [src/dynavec/cli.py](../src/dynavec/cli.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/next.config.mjs](../dashboard/next.config.mjs) | Add dynavec dashboard --port, documented telemetry wiring and static bundle serving/browser launch. Test startup, port conflicts and API integration; depend on the security model in #137. |
-| [#127 — Telemetry data model: DynamoDB events table](https://github.com/codeforstartups/dynavec/issues/127)
unassigned | P2 | [src/dynavec/telemetry.py](../src/dynavec/telemetry.py)
[src/dynavec/provisioning.py](../src/dynavec/provisioning.py) | TelemetryRecorder is an in-memory ring buffer. Add an opt-in DynamoDB event schema/store with TTL, bounded asynchronous writes, provisioning and privacy documentation. |
-| [#129 — Dashboard view: Resources (buckets, indexes, tables)](https://github.com/codeforstartups/dynavec/issues/129)
unassigned | P2 | [src/dynavec/client.py](../src/dynavec/client.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx) | Reuse describe/list helpers in a resources API and dashboard cards for buckets/indexes/tables. Add pagination, empty/error states and tests; there is no /api/resources route today. |
-| [#130 — Dashboard view: Namespace explorer](https://github.com/codeforstartups/dynavec/issues/130)
unassigned | P2 | [src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx) | Share one namespace-explorer implementation with #141. Add paginated counts/size summaries and detail/search UI, avoiding an unbounded full scan for each dashboard refresh. |
-| [#131 — Dashboard view: Cost panel](https://github.com/codeforstartups/dynavec/issues/131)
@tikhandesanket | P2 | [benchmarks/cost_model.py](../benchmarks/cost_model.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx) | Expose the existing cost model through a cost API using live size/volume inputs, then build a breakdown panel. Show assumptions, pricing date and an explicit estimate label. |
-| [#141 — Dashboard: Add Namespace Explorer](https://github.com/codeforstartups/dynavec/issues/141)
@osamashabih6960 | P2 | [dashboard/components/Sidebar.tsx](../dashboard/components/Sidebar.tsx)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py) | This overlaps #130. Agree on one owner/acceptance checklist and implement one namespace list, counts, filter and detail flow; link or consolidate the duplicate issue. |
-| [#286 — Prompt templates & output parsers](https://github.com/codeforstartups/dynavec/issues/286)
@tikhandesanket | P2 | [src/dynavec/chat](../src/dynavec/chat)
[src/dynavec/nodes/builtin.py](../src/dynavec/nodes/builtin.py) | LLMNode accepts messages, but a reusable template/output-parser module is absent. Add message templates and JSON/Pydantic parsers with clear validation errors and rendering/parser tests. |
-| [#291 — Visual builder: ReactFlow canvas to compose orchestrations](https://github.com/codeforstartups/dynavec/issues/291)
unassigned | P2 | [dashboard/package.json](../dashboard/package.json)
[dashboard/app/page.tsx](../dashboard/app/page.tsx)
[src/dynavec/nodes/registry.py](../src/dynavec/nodes/registry.py) | Choose dashboard versus standalone ownership, then implement a ReactFlow canvas using the node registry and #282 graph schema. Test create/connect/configure/save/reload and incompatible ports. |
-| [#292 — Code ⇄ visual round-trip (import/export graph as code)](https://github.com/codeforstartups/dynavec/issues/292)
unassigned | P2 | [src/dynavec/nodes/base.py](../src/dynavec/nodes/base.py)
[src/dynavec/agents/graph.py](../src/dynavec/agents/graph.py) | Define one versioned graph representation first (#282), then implement SDK/code-to-canvas import/export. Add semantic round-trip tests for nodes, edges, config and custom nodes; do not execute arbitrary source to import it. |
-| [#293 — Live run visualization on the canvas](https://github.com/codeforstartups/dynavec/issues/293)
unassigned | P2 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[dashboard/app/page.tsx](../dashboard/app/page.tsx) | After #291/#299, stream the same run/span events to canvas nodes. Test start/finish/error state, reconnects and inputs/outputs without creating a second telemetry format. |
-| [#294 — Debug & iterate: step through, re-run a single node, pin test inputs](https://github.com/codeforstartups/dynavec/issues/294)
unassigned | P3 | [src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py)
[dashboard/app/page.tsx](../dashboard/app/page.tsx) | Build on #281/#291: add step/pause, pinned inputs and single-node replay with inspectable edge state. Test reproducibility and preservation of the original run. |
-| [#297 — Durable execution on Step Functions / Workflows](https://github.com/codeforstartups/dynavec/issues/297)
unassigned | P2 | [src/dynavec/agents/graph.py](../src/dynavec/agents/graph.py)
[src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py) | Specify a durable execution backend around stable graph/state interfaces, then implement Step Functions/Workflows adapters with idempotent steps, retries and resume tests. DynamoDB checkpoints alone do not provide managed execution. |
-| [#298 — One-command serverless deploy (Lambda / Cloud Run), in-account](https://github.com/codeforstartups/dynavec/issues/298)
unassigned | P2 | [docs/DEPLOY.md](../docs/DEPLOY.md)
[src/dynavec/cli.py](../src/dynavec/cli.py) | The deployment document is not a one-command agent deployment flow. Add packaging and a deploy command with an explicit resource/IAM plan; verify provision/run/log/teardown in a dedicated cloud test environment. |
-| [#307 — Multi-cloud: pluggable GCP backends (Vertex, Firestore, Cloud Run, Workflows)](https://github.com/codeforstartups/dynavec/issues/307)
unassigned | P3 | [src/dynavec/config.py](../src/dynavec/config.py)
[src/dynavec/stores](../src/dynavec/stores)
[pyproject.toml](../pyproject.toml) | Stabilize provider-neutral store/model/execution interfaces, then implement GCP adapters and a parity matrix. Keep GCP dependencies optional and add contract tests plus a documented cloud smoke run. |
-| [#308 — Multi-cloud: pluggable Azure backends (Azure OpenAI, Cosmos DB, Functions, Durable Functions)](https://github.com/codeforstartups/dynavec/issues/308)
unassigned | P3 | [src/dynavec/config.py](../src/dynavec/config.py)
[src/dynavec/stores](../src/dynavec/stores)
[pyproject.toml](../pyproject.toml) | Reuse the backend contracts from #307, add Azure model/store/compute/durable/secrets adapters as optional extras, and verify an example graph against the published parity matrix. |
-| [#321 — feat(ingest): Support cursor pagination in MCPResourceSource](https://github.com/codeforstartups/dynavec/issues/321)
@neha-5456 | P2 | [src/dynavec/ingest.py](../src/dynavec/ingest.py)
[tests/test_ingest.py](../tests/test_ingest.py) | MCPResourceSource still calls list_resources once. Coordinate with the assignee to follow nextCursor/next_cursor in objects and mappings, forward cursor and test multiple/empty pages plus repeated-cursor protection. |
-
-### Epic (5)
-
-| Issue and snapshot owner | Priority | Evidence | Resolution and acceptance |
-|---|---|---|---|
-| [#122 — [EPIC] Telemetry & resource dashboard](https://github.com/codeforstartups/dynavec/issues/122)
unassigned | P2 | [dashboard/README.md](../dashboard/README.md)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py)
[dashboard/app/page.tsx](../dashboard/app/page.tsx) | Track child completion individually: persistence #127, launch/security #125/#137, resources/namespaces #129/#130/#141, cost #131 and eval UI #136. Reconcile the original Vite plan with the shipped Next.js app. |
-| [#273 — [EPIC] dynaflow — serverless agent orchestration engine + visual builder](https://github.com/codeforstartups/dynavec/issues/273)
unassigned | P2 | [src/dynavec/agents/graph.py](../src/dynavec/agents/graph.py)
[src/dynavec/nodes/graph.py](../src/dynavec/nodes/graph.py)
[src/dynavec/checkpoint.py](../src/dynavec/checkpoint.py)
[docs/roadmap.md](../docs/roadmap.md) | Graph runtimes, nodes, agents and checkpointing have landed. Track remaining schema/round-trip, snapshots, visual builder and deployment children; update the stale roadmap claim that the engine is only pseudocode. |
-| [#274 — [EPIC] dynalogs — in-account observability for agent runs](https://github.com/codeforstartups/dynavec/issues/274)
unassigned | P2 | [src/dynavec/logging.py](../src/dynavec/logging.py)
[src/dynavec/dashboard.py](../src/dynavec/dashboard.py) | Structured run/node logs and query telemetry exist. Complete parented spans, durable run history, metrics and dashboard integration under #299/#301/#302 rather than recreating logging. |
-| [#275 — [EPIC] dynaevals — evaluation, datasets & CI gating](https://github.com/codeforstartups/dynavec/issues/275)
unassigned | P2 | [src/dynavec/eval](../src/dynavec/eval)
[PR #351 open](https://github.com/codeforstartups/dynavec/pull/351) | Retrieval/RAG metrics and trend tracking exist. Finish dataset round-trip #304, review CI gate #305 and attach evals to runs #306; keep the epic open until those flows are verified. |
-| [#276 — [EPIC] Ecosystem launch — landing page & cross-library story (dynavec / dynaflow / dynalogs / dynaevals)](https://github.com/codeforstartups/dynavec/issues/276)
unassigned | P3 | [opensource/dynavec/index.html](../opensource/dynavec/index.html)
[tools/build_docs.py](../tools/build_docs.py)
[docs/roadmap.md](../docs/roadmap.md) | Ecosystem pages and landing-page sections exist after #347. Review #310/#311, then reconcile capability claims with shipped runtime/visual/deployment status before closing the ecosystem epic. |
-
-## Validation and limits
-
-- Python 3.12.15, installed via `uv venv --python 3.12` and the repository's `make install` target.
-- Focused query/store/error tests pass, including real SDK stubs; lint passes for the changed tests.
-- `make run-ci` passed: ruff, strict mypy (87 checked files), **1,125 tests passed, 2 opt-in live AWS tests skipped**, and 1 snapshot passed. The focused store/error/logging selection passed all 100 tests; changed-test lint and `git diff --check` also passed.
-- No live AWS resources, credentialed model calls, external Redis/Memcached clusters, browser layout acceptance, or deployed orchestration were exercised. Missing/partial assessments concern inspected deliverables and explicitly identified acceptance gaps.
-- This is a dated snapshot. Recheck current issue assignments, PR state and source before starting follow-up work. This PR does not close the entire backlog or automatically change issue ownership.