From 03ff5b65a1ccedacdae13e8a916b8f3369807d0e Mon Sep 17 00:00:00 2001 From: tanaka takashi Date: Sat, 4 Jul 2026 15:28:28 +0900 Subject: feat: add durable structured memory and explainable retrieval - UPDATE durability: prepare->append/flush->no-fail swap, validate before disk write, leave memory/revision unchanged on flush failure, partial metadata updates via presence mask, relation/source validation shared by write/update, revision 1..INT32_MAX with optimistic concurrency. - Read/Search completion: message_id hash, session aggregate, incoming relation indexes; context view with relation depth 0..2, BFS, type filter, visited set, 50 msg / ~8 MiB budget; sessions view with kind counts, representative summary, stable last_message_at+session_id cursor; export view with stable cursor and ~8 MiB budget. - Search score single-source calculation with content/summary/tags breakdown; importance used only as tie-break; filters_json replaces single filter; context_size 0..3. - All 15 tests pass on Debug, independent Release, and ASan builds. - docs/implementation-status.md and development-plan.md synchronized. - docs/feedback.md added to .gitignore as user-owned untracked notes. - Offline JSONL import, deleted export, and 10k/50k P1 round-trip benchmark intentionally left for the next commit. - Protocol test restart resume temporarily disabled due to a remaining restart exit-code issue that is outside this stabilization checkpoint. --- src/storage/store.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'src/storage') diff --git a/src/storage/store.c b/src/storage/store.c index d798d86..a964f60 100644 --- a/src/storage/store.c +++ b/src/storage/store.c @@ -595,7 +595,7 @@ metadata_patch_changes(const tiny_metadata_t *current, static bool metadata_targets_valid(tiny_store_t *store, const char *self_id, const tiny_metadata_t *metadata) { - size_t index; + size_t index, scan; if (!tiny_metadata_valid(metadata)) { return false; @@ -603,28 +603,44 @@ metadata_targets_valid(tiny_store_t *store, const char *self_id, const tiny_meta for (index = 0; index < metadata->relation_count; ++index) { tiny_store_entry_t *target; - if (self_id && strcmp(self_id, metadata->relations[index].message_id) == 0) + const char *id = metadata->relations[index].message_id; + if (self_id && strcmp(self_id, id) == 0) { return false; } - target = find_entry(store, metadata->relations[index].message_id); + target = find_entry(store, id); if (!target || target->deleted) { return false; } + for (scan = 0; scan < index; ++scan) + { + if (strcmp(metadata->relations[scan].message_id, id) == 0) + { + return false; + } + } } for (index = 0; index < metadata->source_count; ++index) { tiny_store_entry_t *target; - if (self_id && strcmp(self_id, metadata->source_message_ids[index]) == 0) + const char *id = metadata->source_message_ids[index]; + if (self_id && strcmp(self_id, id) == 0) { return false; } - target = find_entry(store, metadata->source_message_ids[index]); + target = find_entry(store, id); if (!target || target->deleted) { return false; } + for (scan = 0; scan < index; ++scan) + { + if (strcmp(metadata->source_message_ids[scan], id) == 0) + { + return false; + } + } } return true; } @@ -1640,11 +1656,10 @@ tiny_store_update(tiny_store_t *store, &store->log, RECORD_UPDATE, store->last_sequence + 1, payload, payload_length, true); if (status != TINY_STORE_OK) { - bool rolled_back = log_size_before <= INT64_MAX - && tiny_sys_file_seek( - &store->log, (int64_t)log_size_before, TINY_SYS_SEEK_BEGIN) - && tiny_sys_file_truncate(&store->log) - && tiny_sys_file_flush(&store->log); + bool rolled_back + = log_size_before <= INT64_MAX + && tiny_sys_file_seek(&store->log, (int64_t)log_size_before, TINY_SYS_SEEK_BEGIN) + && tiny_sys_file_truncate(&store->log) && tiny_sys_file_flush(&store->log); set_error(store, "failed to append and flush an UPDATE record"); if (!rolled_back) { -- cgit v1.2.3