fix: cross-tier timestamp precision mismatch (client 0.4.3)
Browse files_utc_now_iso() produced 6-digit microsecond timestamps while SQL
DEFAULTs produced 3-digit milliseconds. The width difference broke
lexicographic sorting across tiers ('Z' 0x5A > '3' 0x33 at position
24). Truncated to 3-digit milliseconds matching SQLite output.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
sibyl-memory-client/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ All notable changes to `sibyl-memory-client` are recorded here. Format
|
|
| 4 |
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning
|
| 5 |
follows [SemVer](https://semver.org/).
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
## [0.4.2] - 2026-05-22
|
| 8 |
|
| 9 |
`_sanitize_fts5_query` default mode flipped from phrase-match to
|
|
|
|
| 4 |
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning
|
| 5 |
follows [SemVer](https://semver.org/).
|
| 6 |
|
| 7 |
+
## [0.4.3] - 2026-05-26
|
| 8 |
+
|
| 9 |
+
### Fixed
|
| 10 |
+
|
| 11 |
+
- **Cross-tier timestamp precision mismatch.** `_utc_now_iso()` produced
|
| 12 |
+
6-digit microsecond timestamps (`45.525358Z`) while every SQL DEFAULT
|
| 13 |
+
used SQLite's 3-digit milliseconds (`45.525Z`). The width difference
|
| 14 |
+
broke lexicographic sorting across tiers: `'Z'` (0x5A) > `'3'` (0x33)
|
| 15 |
+
at position 24, so a journal event written 0.358 ms after an entity
|
| 16 |
+
update would sort *before* it in any `ORDER BY ts` merge. Now truncated
|
| 17 |
+
to 3-digit milliseconds to match SQLite output. Affects journal_events,
|
| 18 |
+
revenue_events, error_events, learning_runs.completed_at, and
|
| 19 |
+
skill_proposals.reviewed_at. Existing rows retain their original
|
| 20 |
+
precision (cosmetic, sort-correct within their own tier). Reported by
|
| 21 |
+
external tester smoke test on sibyl-memory-mcp 0.1.2.
|
| 22 |
+
|
| 23 |
## [0.4.2] - 2026-05-22
|
| 24 |
|
| 25 |
`_sanitize_fts5_query` default mode flipped from phrase-match to
|
sibyl-memory-client/README.md
CHANGED
|
@@ -47,13 +47,15 @@ Most agent-memory products store everything on someone else's servers, treat eve
|
|
| 47 |
| Frozen things, kept but out of the way | ARCHIVE | `archive_entity(kind, name)` |
|
| 48 |
| Search across everything | FTS5 | `search_entities(query)` |
|
| 49 |
|
| 50 |
-
## What's in v0.
|
| 51 |
|
| 52 |
- The full five-tier memory model and the API surface above.
|
|
|
|
| 53 |
- Multi-tenant isolation: one machine can hold separate memory for separate identities.
|
| 54 |
- Self-learning module (paid-tier): the agent watches your patterns and proposes reusable skills.
|
| 55 |
- Memory linter (paid-tier): a health check on the local database.
|
| 56 |
- Tier gating: free-tier callers get clear errors pointing at the upgrade page; paid-tier callers get full access.
|
|
|
|
| 57 |
|
| 58 |
## Tier model
|
| 59 |
|
|
|
|
| 47 |
| Frozen things, kept but out of the way | ARCHIVE | `archive_entity(kind, name)` |
|
| 48 |
| Search across everything | FTS5 | `search_entities(query)` |
|
| 49 |
|
| 50 |
+
## What's in v0.4.x
|
| 51 |
|
| 52 |
- The full five-tier memory model and the API surface above.
|
| 53 |
+
- Cross-tier FTS5 search across entities, state, reference, and journal tiers.
|
| 54 |
- Multi-tenant isolation: one machine can hold separate memory for separate identities.
|
| 55 |
- Self-learning module (paid-tier): the agent watches your patterns and proposes reusable skills.
|
| 56 |
- Memory linter (paid-tier): a health check on the local database.
|
| 57 |
- Tier gating: free-tier callers get clear errors pointing at the upgrade page; paid-tier callers get full access.
|
| 58 |
+
- Uniform millisecond timestamp precision across all tiers (v0.4.3).
|
| 59 |
|
| 60 |
## Tier model
|
| 61 |
|
sibyl-memory-client/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "sibyl-memory-client"
|
| 7 |
-
version = "0.4.
|
| 8 |
description = "Local-first agentic memory SDK. SQLite-backed five-tier hierarchical schema, FTS5 search, multi-tenant, with self-learning skill detection and local memory linter. Foundation of the Sibyl Memory Plugin family."
|
| 9 |
authors = [{ name = "SIBYL, Sibyl Labs LLC", email = "sibyl@sibyllabs.org" }]
|
| 10 |
license = { text = "MIT" }
|
|
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "sibyl-memory-client"
|
| 7 |
+
version = "0.4.3"
|
| 8 |
description = "Local-first agentic memory SDK. SQLite-backed five-tier hierarchical schema, FTS5 search, multi-tenant, with self-learning skill detection and local memory linter. Foundation of the Sibyl Memory Plugin family."
|
| 9 |
authors = [{ name = "SIBYL, Sibyl Labs LLC", email = "sibyl@sibyllabs.org" }]
|
| 10 |
license = { text = "MIT" }
|
sibyl-memory-client/src/sibyl_memory_client/storage.py
CHANGED
|
@@ -40,13 +40,15 @@ _DB_SIDECAR_SUFFIXES = ("-wal", "-shm")
|
|
| 40 |
|
| 41 |
|
| 42 |
def _utc_now_iso() -> str:
|
| 43 |
-
"""Return current UTC time in ISO 8601
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
| 50 |
|
| 51 |
|
| 52 |
def new_id() -> str:
|
|
|
|
| 40 |
|
| 41 |
|
| 42 |
def _utc_now_iso() -> str:
|
| 43 |
+
"""Return current UTC time in ISO 8601 millisecond-precision format.
|
| 44 |
+
|
| 45 |
+
Matches the 3-digit precision of SQLite's ``strftime('%f')`` so that
|
| 46 |
+
timestamps produced by Python and by SQL DEFAULTs sort identically in
|
| 47 |
+
lexicographic comparisons. Prior versions emitted 6-digit microseconds
|
| 48 |
+
which broke cross-tier ``ORDER BY ts`` merges ('Z' > '3' at position 24).
|
| 49 |
+
Fixed in 0.4.3."""
|
| 50 |
+
now = datetime.now(timezone.utc)
|
| 51 |
+
return now.strftime("%Y-%m-%dT%H:%M:%S.") + f"{now.microsecond // 1000:03d}Z"
|
| 52 |
|
| 53 |
|
| 54 |
def new_id() -> str:
|