From 66ad1940cacf30be215887ac660fde864ea7cd0b Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Mon, 29 Sep 2025 05:26:09 -0700 Subject: [PATCH 1/6] chore(logs): capture post-merge status and update ledger --- AGENTS.md | 8 ++++++++ docs/activity/2025-09.md | 6 ++++++ docs/features/Features_Ledger.md | 6 +++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index abb9d20a..e4e1b4fb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -415,6 +415,14 @@ Every outbound port ships with: (a) production adapter, (b) deterministic fake u See archives under `docs/activity/` for older logs. +### 2025-09-29 +- Merged PR #173 after tightening the remaining safe-string usage and OID helper semantics; rebased on `origin/main` and created `feat/next-hex-task` for the follow-up work. +- Problems solved today: CodeRabbit’s final cache-query/string-safety nits and the lingering `gm_oid_is_zero` ambiguity. +- Problems still open: regression tests + docs for strict equality (GM.MVP.T001), and the outstanding libgit2 call sites lingering in CLI/bench helpers. +- Immediate next moves: scope the equality regression tests/doc updates and queue the next hex slice on the new branch. +- Unscheduled-but-important: worksheet automation cleanup and the CLI safety test expansion still need dedicated time before month end. +- Context reminder: capture major decisions in the ledger/worksheet quickly—we’re pushing the context window, so keep notes concise. + ### 2025-09-27 - Routed cache metadata, query, and journal flows through `gm_git_repository_port`; reader/writer now use the adapter for head detection, commit walking, message bodies, and ref updates. - Added port surface area (head lookup, commit walk/message helpers, parent-aware commit spec) plus libgit2 adapter support to keep hex seams consistent. diff --git a/docs/activity/2025-09.md b/docs/activity/2025-09.md index 4f31d007..f5e23bf8 100644 --- a/docs/activity/2025-09.md +++ b/docs/activity/2025-09.md @@ -25,6 +25,12 @@ last_updated: 2025-09-29 - GM OID utilities: added `gm_oid_from_raw`, `gm_oid_from_hex`, `gm_oid_equal`, and `gm_oid_is_zero`, then rewired edge encode/decode, cache metadata checks, hooks, and the journal writer to consume the port-friendly helpers (no direct libgit2 helpers remain in those paths). - Cache stability: zero-initialize cache metadata before reuse to avoid garbage OID bytes and eliminate the sporadic cache-query assertion we hit during `make ci-local`. - Test update sweep: migrated unit tests off `git_oid_fromraw/git_oid_cmp` to the new helpers and asserted return codes so fixtures stay aligned with the hexagonal seams. +- Post-merge wrap-up: + - Solved today: reconciled CodeRabbit feedback (safe string usage in cache query tests, `gm_oid_is_zero` semantics) and merged PR #173; local branch rebased on `origin/main`. + - Still outstanding: finish the strict-equality doc/test tasks (regression coverage + docs in `GM.MVP.T001`) and chase remaining libgit2 call sites in CLI/bench utilities. + - Immediate next move: start `feat/next-hex-task` off `origin/main`, scope the follow-up equality/documentation work, and prep the next hex migration slice. + - Unscheduled but important: schedule time for worksheet automation cleanup and the CLI safety test expansion before month end. + - Personal note: context window is getting crowded—remember to summarize outstanding decisions before diving into the next refactor. ## 2025-09-20 diff --git a/docs/features/Features_Ledger.md b/docs/features/Features_Ledger.md index c0b769f1..afeea4f7 100644 --- a/docs/features/Features_Ledger.md +++ b/docs/features/Features_Ledger.md @@ -5,7 +5,7 @@ audience: [contributors] domain: [planning] tags: [features, progress] status: draft -last_updated: 2025-09-16 +last_updated: 2025-09-29 --- # Features Ledger @@ -137,8 +137,8 @@ Minimum Viable Product - [ ] (GM.MVP.T001) Finalize strict equality -> [!INFO]- (0/4) ⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀ — Step-by-step plan · 0% -> - [ ] Audit equality helpers in cache and journal to ensure OID-first comparisons never fall back when OIDs exist. +> [!INFO]- (1/4) ⣿⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀ — Step-by-step plan · 25% +> - [x] Audit equality helpers in cache and journal to ensure OID-first comparisons never fall back when OIDs exist. > - [ ] Add regression tests covering mixed OID/string paths across cache and journal utilities. > - [ ] Update `docs/TECHNICAL.md` and any relevant ADRs to document strict equality behavior. > - [ ] Sweep references to legacy SHA fallbacks in docs and CLI examples. From bc1fc319303bf1a00b43b03a40992ba788996b1a Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Mon, 29 Sep 2025 06:11:29 -0700 Subject: [PATCH 2/6] test(cache): add strict equality regression coverage --- core/tests/unit/test_cache_strict_equality.c | 332 ++++++++++++++++++ core/tests/unit/test_journal_mixed_cbor.c | 69 +++- docs/TECHNICAL.md | 15 +- docs/activity/2025-09.md | 5 +- .../deprecations/legacy-sha-fields.md | 10 +- docs/cli/combined_markdown.md | 36 +- docs/cli/gitmind-cache-rebuild.md | 8 +- docs/cli/gitmind-install-hooks.md | 12 +- docs/cli/gitmind-link.md | 8 +- docs/cli/gitmind-list.md | 4 +- docs/cli/gitmind.md | 6 +- docs/features/Features_Ledger.md | 8 +- meson.build | 7 + 13 files changed, 456 insertions(+), 64 deletions(-) create mode 100644 core/tests/unit/test_cache_strict_equality.c diff --git a/core/tests/unit/test_cache_strict_equality.c b/core/tests/unit/test_cache_strict_equality.c new file mode 100644 index 00000000..f98673ae --- /dev/null +++ b/core/tests/unit/test_cache_strict_equality.c @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */ +/* © 2025 J. Kirby Ross / Neuroglyph Collective */ + +#include +#include +#include +#include +#include +#include + +#include "gitmind/cache.h" +#include "gitmind/constants_internal.h" +#include "gitmind/context.h" +#include "gitmind/error.h" +#include "gitmind/result.h" +#include "gitmind/security/memory.h" +#include "gitmind/util/oid.h" + +typedef struct test_git_repo_port { + gm_git_repository_port_t port; + gm_git_reference_tip_t cache_tip; + gm_git_reference_tip_t journal_tips[3]; + size_t journal_tip_count; + size_t journal_tip_index; + gm_git_reference_tip_t glob_tip; +} test_git_repo_port_t; + +static gm_result_void_t stub_repository_path(void *self, + gm_git_repository_path_kind_t kind, + char *out_buffer, + size_t buffer_size) { + (void)self; + (void)kind; + (void)out_buffer; + (void)buffer_size; + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static gm_result_void_t stub_head_branch(void *self, char *out_name, + size_t out_name_size) { + (void)self; + (void)out_name; + (void)out_name_size; + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static gm_result_void_t stub_build_tree(void *self, const char *dir_path, + gm_oid_t *out_tree_oid) { + (void)self; + (void)dir_path; + (void)out_tree_oid; + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static gm_result_void_t reference_tip(void *self, const char *ref_name, + gm_git_reference_tip_t *out_tip) { + test_git_repo_port_t *stub = (test_git_repo_port_t *)self; + if (ref_name == NULL || out_tip == NULL) { + return gm_err_void(GM_ERROR(GM_ERR_INVALID_ARGUMENT, + "ref_name and out_tip required")); + } + + if (strncmp(ref_name, GM_CACHE_REF_PREFIX, + strlen(GM_CACHE_REF_PREFIX)) == 0) { + *out_tip = stub->cache_tip; + return gm_ok_void(); + } + + if (strncmp(ref_name, GITMIND_EDGES_REF_PREFIX, + strlen(GITMIND_EDGES_REF_PREFIX)) == 0) { + if (stub->journal_tip_count == 0U) { + gm_memset_safe(out_tip, sizeof(*out_tip), 0, sizeof(*out_tip)); + return gm_ok_void(); + } + size_t index = stub->journal_tip_index; + if (index >= stub->journal_tip_count) { + index = stub->journal_tip_count - 1U; + } + *out_tip = stub->journal_tips[index]; + if (stub->journal_tip_index + 1U < stub->journal_tip_count) { + stub->journal_tip_index += 1U; + } + return gm_ok_void(); + } + + gm_memset_safe(out_tip, sizeof(*out_tip), 0, sizeof(*out_tip)); + return gm_ok_void(); +} + +static gm_result_void_t reference_glob_latest(void *self, const char *pattern, + gm_git_reference_tip_t *out_tip) { + test_git_repo_port_t *stub = (test_git_repo_port_t *)self; + if (pattern == NULL || out_tip == NULL) { + return gm_err_void(GM_ERROR(GM_ERR_INVALID_ARGUMENT, + "pattern and out_tip required")); + } + (void)pattern; + *out_tip = stub->glob_tip; + return gm_ok_void(); +} + +static gm_result_void_t stub_commit_read_blob(void *self, + const gm_oid_t *commit_oid, + const char *path, + uint8_t **out_data, + size_t *out_size) { + (void)self; + (void)commit_oid; + (void)path; + if (out_data != NULL) { + *out_data = NULL; + } + if (out_size != NULL) { + *out_size = 0U; + } + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static void blob_dispose(void *self, uint8_t *data, size_t size) { + (void)self; + (void)size; + free(data); +} + +static gm_result_void_t stub_commit_read_message(void *self, + const gm_oid_t *commit_oid, + char **out_message) { + (void)self; + (void)commit_oid; + if (out_message != NULL) { + *out_message = NULL; + } + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static void stub_commit_message_dispose(void *self, char *message) { + (void)self; + free(message); +} + +static gm_result_void_t stub_walk_commits(void *self, const char *ref_name, + gm_git_commit_visit_cb visit_callback, + void *userdata) { + (void)self; + (void)ref_name; + (void)visit_callback; + (void)userdata; + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static gm_result_void_t stub_commit_tree_size(void *self, + const gm_oid_t *commit_oid, + uint64_t *out_size_bytes) { + (void)self; + (void)commit_oid; + if (out_size_bytes != NULL) { + *out_size_bytes = 0U; + } + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static gm_result_void_t stub_commit_create(void *self, + const gm_git_commit_spec_t *spec, + gm_oid_t *out_commit_oid) { + (void)self; + (void)spec; + (void)out_commit_oid; + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static gm_result_void_t stub_reference_update( + void *self, const gm_git_reference_update_spec_t *spec) { + (void)self; + (void)spec; + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static gm_result_void_t stub_resolve_blob_at_head(void *self, const char *path, + gm_oid_t *out_blob_oid) { + (void)self; + (void)path; + (void)out_blob_oid; + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static gm_result_void_t stub_resolve_blob_at_commit(void *self, + const gm_oid_t *commit_oid, + const char *path, + gm_oid_t *out_blob_oid) { + (void)self; + (void)commit_oid; + (void)path; + (void)out_blob_oid; + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static gm_result_void_t stub_commit_parent_count(void *self, + const gm_oid_t *commit_oid, + size_t *out_parent_count) { + (void)self; + (void)commit_oid; + if (out_parent_count != NULL) { + *out_parent_count = 0U; + } + return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); +} + +static const gm_git_repository_port_vtbl_t TEST_PORT_VTBL = { + .repository_path = stub_repository_path, + .head_branch = stub_head_branch, + .build_tree_from_directory = stub_build_tree, + .reference_tip = reference_tip, + .reference_glob_latest = reference_glob_latest, + .commit_read_blob = stub_commit_read_blob, + .blob_dispose = blob_dispose, + .commit_read_message = stub_commit_read_message, + .commit_message_dispose = stub_commit_message_dispose, + .walk_commits = stub_walk_commits, + .commit_tree_size = stub_commit_tree_size, + .commit_create = stub_commit_create, + .reference_update = stub_reference_update, + .resolve_blob_at_head = stub_resolve_blob_at_head, + .resolve_blob_at_commit = stub_resolve_blob_at_commit, + .commit_parent_count = stub_commit_parent_count, +}; + +static void init_stub(test_git_repo_port_t *stub) { + gm_memset_safe(stub, sizeof(*stub), 0, sizeof(*stub)); + stub->port.vtbl = &TEST_PORT_VTBL; + stub->port.self = stub; +} + +static void set_oid_from_hex(gm_oid_t *oid, const char *hex) { + int rc = gm_oid_from_hex(oid, hex); + assert(rc == GM_OK); +} + +static void set_tip(gm_git_reference_tip_t *tip, const char *hex, + uint64_t commit_time, bool has_target, bool zero_binary) { + gm_memset_safe(tip, sizeof(*tip), 0, sizeof(*tip)); + tip->has_target = has_target; + tip->commit_time = commit_time; + if (!zero_binary && hex != NULL) { + set_oid_from_hex(&tip->oid, hex); + } + if (hex != NULL) { + size_t len = strlen(hex); + if (len >= sizeof(tip->oid_hex)) { + len = sizeof(tip->oid_hex) - 1U; + } + memcpy(tip->oid_hex, hex, len); + tip->oid_hex[len] = '\0'; + } +} + +static void fill_hex(char *buffer, size_t buffer_len, char digit) { + for (size_t i = 0; i + 1U < buffer_len; ++i) { + buffer[i] = digit; + } + buffer[buffer_len - 1U] = '\0'; +} + +static void reset_journal_sequence(test_git_repo_port_t *stub) { + stub->journal_tip_index = 0U; +} + +int main(void) { + printf("test_cache_strict_equality... "); + + time_t now = time(NULL); + assert(now > 0); + + /* Scenario 1: OID-first equality ignores mismatched legacy hex */ + test_git_repo_port_t stub1; + init_stub(&stub1); + stub1.journal_tip_count = 2U; + + set_tip(&stub1.cache_tip, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + (uint64_t)now, true, false); + stub1.glob_tip = stub1.cache_tip; + + set_tip(&stub1.journal_tips[0], "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + (uint64_t)now, true, false); + fill_hex(stub1.journal_tips[0].oid_hex, + sizeof stub1.journal_tips[0].oid_hex, 'c'); + + set_tip(&stub1.journal_tips[1], "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + (uint64_t)now, true, false); + + gm_context_t ctx1 = {0}; + ctx1.git_repo_port = stub1.port; + + bool stale = gm_cache_is_stale(&ctx1, "main"); + assert(stale == false); + + /* Scenario 2: OID-first equality detects mismatch */ + reset_journal_sequence(&stub1); + set_tip(&stub1.journal_tips[1], "cccccccccccccccccccccccccccccccccccccccc", + (uint64_t)now, true, false); + stale = gm_cache_is_stale(&ctx1, "main"); + assert(stale == true); + + /* Scenario 3: fallback uses hex string when binary OID missing */ + test_git_repo_port_t stub2; + init_stub(&stub2); + stub2.journal_tip_count = 2U; + + set_tip(&stub2.cache_tip, "dddddddddddddddddddddddddddddddddddddddd", + (uint64_t)now, true, false); + stub2.glob_tip = stub2.cache_tip; + + set_tip(&stub2.journal_tips[0], "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + (uint64_t)now, true, true); + set_tip(&stub2.journal_tips[1], "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + (uint64_t)now, true, false); + + gm_context_t ctx2 = {0}; + ctx2.git_repo_port = stub2.port; + + stale = gm_cache_is_stale(&ctx2, "develop"); + assert(stale == false); + + /* Scenario 4: fallback detects mismatch when hex differs */ + reset_journal_sequence(&stub2); + set_tip(&stub2.journal_tips[1], "ffffffffffffffffffffffffffffffffffffffff", + (uint64_t)now, true, false); + stale = gm_cache_is_stale(&ctx2, "develop"); + assert(stale == true); + + printf("OK\n"); + return 0; +} diff --git a/core/tests/unit/test_journal_mixed_cbor.c b/core/tests/unit/test_journal_mixed_cbor.c index 4a15a2d1..9ac9fb12 100644 --- a/core/tests/unit/test_journal_mixed_cbor.c +++ b/core/tests/unit/test_journal_mixed_cbor.c @@ -21,13 +21,40 @@ typedef struct { size_t count; -} count_ctx_t; + gm_edge_t last; +} basic_ctx_t; -static int count_basic_cb(const gm_edge_t *edge, void *ud) { - (void)edge; count_ctx_t *c = (count_ctx_t *)ud; c->count++; return 0; +typedef struct { + size_t count; + gm_edge_attributed_t last; +} attr_ctx_t; + +static int capture_basic_cb(const gm_edge_t *edge, void *ud) { + if (edge == NULL || ud == NULL) { + return GM_ERR_INVALID_ARGUMENT; + } + basic_ctx_t *ctx = (basic_ctx_t *)ud; + int copy_rc = gm_memcpy_span(&ctx->last, sizeof(ctx->last), edge, + sizeof(*edge)); + if (copy_rc != GM_OK) { + return copy_rc; + } + ctx->count++; + return GM_OK; } -static int count_attr_cb(const gm_edge_attributed_t *edge, void *ud) { - (void)edge; count_ctx_t *c = (count_ctx_t *)ud; c->count++; return 0; + +static int capture_attr_cb(const gm_edge_attributed_t *edge, void *ud) { + if (edge == NULL || ud == NULL) { + return GM_ERR_INVALID_ARGUMENT; + } + attr_ctx_t *ctx = (attr_ctx_t *)ud; + int copy_rc = gm_memcpy_span(&ctx->last, sizeof(ctx->last), edge, + sizeof(*edge)); + if (copy_rc != GM_OK) { + return copy_rc; + } + ctx->count++; + return GM_OK; } int main(void) { @@ -52,25 +79,26 @@ int main(void) { git_config_free(cfg); /* Build mixed CBOR payload: one basic, one attributed */ + uint8_t rawA[GM_OID_RAWSZ]; memset(rawA, 0xCC, sizeof rawA); + uint8_t rawB[GM_OID_RAWSZ]; memset(rawB, 0xDD, sizeof rawB); + gm_edge_t e = {0}; + assert(gm_memcpy_span(e.src_sha, sizeof e.src_sha, rawA, sizeof rawA) == GM_OK); + assert(gm_memcpy_span(e.tgt_sha, sizeof e.tgt_sha, rawB, sizeof rawB) == GM_OK); assert(gm_strcpy_safe(e.src_path, sizeof e.src_path, "src/A.c") == GM_OK); assert(gm_strcpy_safe(e.tgt_path, sizeof e.tgt_path, "src/B.c") == GM_OK); e.rel_type = GM_REL_IMPLEMENTS; e.confidence = 0x3C00; e.timestamp = 7; - uint8_t rawA[GM_OID_RAWSZ]; memset(rawA, 0xCC, sizeof rawA); - uint8_t rawB[GM_OID_RAWSZ]; memset(rawB, 0xDD, sizeof rawB); - assert(gm_oid_from_raw(&e.src_oid, rawA, sizeof rawA) == GM_OK); - assert(gm_oid_from_raw(&e.tgt_oid, rawB, sizeof rawB) == GM_OK); gm_result_ulid_t u = gm_ulid_generate(e.ulid); assert(u.ok); uint8_t buf1[512]; size_t len1 = sizeof buf1; assert(gm_edge_encode_cbor(&e, buf1, &len1).ok); gm_edge_attributed_t ae = {0}; + assert(gm_memcpy_span(ae.src_sha, sizeof ae.src_sha, rawA, sizeof rawA) == GM_OK); + assert(gm_memcpy_span(ae.tgt_sha, sizeof ae.tgt_sha, rawB, sizeof rawB) == GM_OK); assert(gm_strcpy_safe(ae.src_path, sizeof ae.src_path, "docs/A.md") == GM_OK); assert(gm_strcpy_safe(ae.tgt_path, sizeof ae.tgt_path, "src/C.c") == GM_OK); ae.rel_type = GM_REL_REFERENCES; ae.confidence = 0x1C00; ae.timestamp = 8; - assert(gm_oid_from_raw(&ae.src_oid, rawA, sizeof rawA) == GM_OK); - assert(gm_oid_from_raw(&ae.tgt_oid, rawB, sizeof rawB) == GM_OK); ae.attribution.source_type = GM_SOURCE_AI_CLAUDE; assert(gm_strcpy_safe(ae.attribution.author, sizeof ae.attribution.author, "claude@local") == GM_OK); @@ -94,11 +122,24 @@ int main(void) { rc = gm_journal_create_commit(&ctx, "refs/gitmind/edges/test", payload, len1 + len2); assert(rc == GM_OK); - count_ctx_t cbasic = {0}, cattr = {0}; - rc = gm_journal_read(&ctx, "test", count_basic_cb, &cbasic); + basic_ctx_t cbasic = {0}; + attr_ctx_t cattr = {0}; + gm_oid_t expected_src; assert(gm_oid_from_raw(&expected_src, rawA, sizeof rawA) == GM_OK); + gm_oid_t expected_tgt; assert(gm_oid_from_raw(&expected_tgt, rawB, sizeof rawB) == GM_OK); + + rc = gm_journal_read(&ctx, "test", capture_basic_cb, &cbasic); assert(rc == GM_OK && cbasic.count >= 1); - rc = gm_journal_read_attributed(&ctx, "test", count_attr_cb, &cattr); + assert(!gm_oid_is_zero(&cbasic.last.src_oid)); + assert(!gm_oid_is_zero(&cbasic.last.tgt_oid)); + assert(gm_oid_equal(&cbasic.last.src_oid, &expected_src)); + assert(gm_oid_equal(&cbasic.last.tgt_oid, &expected_tgt)); + + rc = gm_journal_read_attributed(&ctx, "test", capture_attr_cb, &cattr); assert(rc == GM_OK && cattr.count >= 1); + assert(!gm_oid_is_zero(&cattr.last.src_oid)); + assert(!gm_oid_is_zero(&cattr.last.tgt_oid)); + assert(gm_oid_equal(&cattr.last.src_oid, &expected_src)); + assert(gm_oid_equal(&cattr.last.tgt_oid, &expected_tgt)); if (ctx.git_repo_port_dispose != NULL) { ctx.git_repo_port_dispose(&ctx.git_repo_port); diff --git a/docs/TECHNICAL.md b/docs/TECHNICAL.md index 13ef2261..b6f2c13d 100644 --- a/docs/TECHNICAL.md +++ b/docs/TECHNICAL.md @@ -5,7 +5,7 @@ audience: [developers] domain: [architecture, project] tags: [architecture, journal, cache, oid, engineering] status: stable -last_updated: 2025-09-15 +last_updated: 2025-09-29 --- # Technical Overview @@ -16,7 +16,8 @@ This page orients engineers to the core implementation and points to deeper docs - Journal‑first storage: semantic edges are CBOR commits under `refs/gitmind/edges/`. - Rebuildable cache: high‑performance Roaring Bitmap cache under `refs/gitmind/cache/`. -- OID‑first APIs: new interfaces use `gm_oid_t` (`git_oid`) and compare via `git_oid_cmp`. +- OID‑first APIs: new interfaces use `gm_oid_t` and compare via `gm_oid_equal`, + falling back to SHA hex only when a binary OID is unavailable. - Append‑only semantics: edges form an OR‑Set via ULIDs; merges resolve predictably. See `docs/architecture/` for full write‑ups: @@ -55,7 +56,7 @@ Header guards lint: `meson run -C build lint_header_guards` - Complete OID‑only on‑disk cache migration and readers. - Extend CBOR schema to store OIDs explicitly. -- Add focused tests: CBOR base64 round‑trip, `gm_snprintf` truncation, OID equality paths. +- Add focused tests: CBOR base64 round‑trip and `gm_snprintf` truncation paths. ## Engineering Principles @@ -67,13 +68,17 @@ Header guards lint: `meson run -C build lint_header_guards` See also: `docs/architecture/MIGRATION_PHILOSOPHY.md`. -## Recent Changes (2025‑09‑15) +## Recent Changes (2025‑09‑29) +- Cache/journal strict equality guardrails: regression tests now assert binary + OIDs are compared via `gm_oid_equal` before falling back to SHA hex in both + the cache staleness check and journal CBOR decoder paths. - OID‑first cache metadata: added binary `journal_tip_oid_bin`; hex string retained for porcelain only. - Ref building standardized via `gm_build_ref` with branch validation; all refs use `GITMIND_EDGES_REF_PREFIX`. - Unsafe libc calls replaced in core with safe wrappers (`gm_memset_safe`, `gm_memcpy_span`, `gm_strcpy_safe`, `gm_snprintf`). - OID formatting/prefixing now uses libgit2 (`git_oid_fmt`, `git_oid_tostr`). - Hashing: improved internal OID hash mixing for more resilient distribution. - Result constructors annotated `[[nodiscard]]`; more API annotations added in cache/journal/CBOR headers. -- New tests: ref utils, OID fallback equality, cache shard distribution. +- New tests: ref utils, OID fallback equality, cache shard distribution, + cache/journal strict equality regressions. - CBOR debug flag: `GITMIND_CBOR_DEBUG=1` enables decode tracing in the journal reader. diff --git a/docs/activity/2025-09.md b/docs/activity/2025-09.md index f5e23bf8..ca612c09 100644 --- a/docs/activity/2025-09.md +++ b/docs/activity/2025-09.md @@ -25,9 +25,12 @@ last_updated: 2025-09-29 - GM OID utilities: added `gm_oid_from_raw`, `gm_oid_from_hex`, `gm_oid_equal`, and `gm_oid_is_zero`, then rewired edge encode/decode, cache metadata checks, hooks, and the journal writer to consume the port-friendly helpers (no direct libgit2 helpers remain in those paths). - Cache stability: zero-initialize cache metadata before reuse to avoid garbage OID bytes and eliminate the sporadic cache-query assertion we hit during `make ci-local`. - Test update sweep: migrated unit tests off `git_oid_fromraw/git_oid_cmp` to the new helpers and asserted return codes so fixtures stay aligned with the hexagonal seams. +- Strict equality follow-through: + - Added cache/journal regression tests covering binary-first comparisons and string fallbacks. + - Updated `docs/TECHNICAL.md`, refreshed CLI docs to speak OID-first, and adjusted the legacy SHA deprecation ADR; Features Ledger now tracks GM.MVP.T001 at 100% complete. - Post-merge wrap-up: - Solved today: reconciled CodeRabbit feedback (safe string usage in cache query tests, `gm_oid_is_zero` semantics) and merged PR #173; local branch rebased on `origin/main`. - - Still outstanding: finish the strict-equality doc/test tasks (regression coverage + docs in `GM.MVP.T001`) and chase remaining libgit2 call sites in CLI/bench utilities. + - Still outstanding: chase remaining libgit2 call sites in CLI/bench utilities. - Immediate next move: start `feat/next-hex-task` off `origin/main`, scope the follow-up equality/documentation work, and prep the next hex migration slice. - Unscheduled but important: schedule time for worksheet automation cleanup and the CLI safety test expansion before month end. - Personal note: context window is getting crowded—remember to summarize outstanding decisions before diving into the next refactor. diff --git a/docs/architecture/deprecations/legacy-sha-fields.md b/docs/architecture/deprecations/legacy-sha-fields.md index 015d1719..baf16349 100644 --- a/docs/architecture/deprecations/legacy-sha-fields.md +++ b/docs/architecture/deprecations/legacy-sha-fields.md @@ -5,7 +5,7 @@ audience: [developers] domain: [architecture] tags: [deprecation, oid] status: planned -last_updated: 2025-09-15 +last_updated: 2025-09-29 --- # Deprecation Notice: Legacy SHA-1 Byte Arrays in Edges @@ -44,7 +44,8 @@ We have migrated all encode/decode, cache, hooks, and equality semantics to be O ## Migration Plan 1. Grace period: continue to write OID fields and (for backward compatibility) read legacy fields in decoders when OIDs are absent. -2. Consumers should use `gm_oid_t` (`git_oid`) exclusively and compare via `git_oid_cmp`. +2. Consumers should use `gm_oid_t` exclusively and compare via `gm_oid_equal`, + falling back to legacy SHA bytes only when a binary OID is unavailable. 3. After one minor release, remove `src_sha`/`tgt_sha` from public structs and drop legacy CBOR keys from writers (readers may retain legacy acceptance for another cycle if needed). ## Action Items @@ -55,4 +56,7 @@ We have migrated all encode/decode, cache, hooks, and equality semantics to be O ## Compatibility -Journal and cache already use OID‑first encoding and compare semantics. Decoders backfill OIDs from legacy bytes, so older commits remain readable throughout the grace period. +Journal and cache already use OID‑first encoding and compare semantics. Decoders +backfill OIDs from legacy bytes, and regression tests (2025‑09‑29) cover both +the cache staleness checks and journal readers to ensure binary OIDs stay the +source of truth while legacy SHA data remains readable during the grace period. diff --git a/docs/cli/combined_markdown.md b/docs/cli/combined_markdown.md index 3f78c969..c62b56e8 100644 --- a/docs/cli/combined_markdown.md +++ b/docs/cli/combined_markdown.md @@ -140,10 +140,10 @@ Your semantic links live as CBOR-encoded Git commits in `refs/gitmind/edges/`, sharded into 256 buckets using the first byte of each SHA. +These are stored as Git tree objects under `refs/gitmind/cache/`, sharded into 256 buckets using the first byte of each OID. Legacy CLI output may still print “SHA” while the rename settles. ### The Magic: Roaring Bitmaps @@ -231,7 +231,7 @@ The cache automatically detects when it's stale by comparing the journal tip wit refs/gitmind/cache/main └── tree ├── meta.json # Cache metadata - ├── 00/ # SHA prefix shard + ├── 00/ # OID prefix shard │ ├── 00a1b2c3.forward │ └── 00a1b2c3.reverse ├── 01/ @@ -315,11 +315,11 @@ The install-hooks command sets up a post-commit hook that automatically creates ## THE CIRCLE OF CHANGE -When you edit a file that has semantic links, those links point to a specific version (blob SHA). After your edit, the old links become outdated. The AUGMENTS system preserves the connection by creating evolution edges: +When you edit a file that has semantic links, those links point to a specific version (blob object ID, or OID). After your edit, the old links become outdated. The AUGMENTS system preserves the connection by creating evolution edges: ``` -Before edit: design.md ──implements──> main.c (SHA: abc123) -After edit: design.md ──implements──> main.c (SHA: def456) +Before edit: design.md ──implements──> main.c (OID: abc123) +After edit: design.md ──implements──> main.c (OID: def456) main.c:abc123 ──AUGMENTS──> main.c:def456 ``` @@ -395,7 +395,7 @@ fi After every commit, the hook: 1. Gets the list of modified files -2. For each file, finds its old blob SHA (before commit) +2. For each file, finds its old blob OID (before commit) 3. Searches recent edges (last 200) for links to that blob 4. Creates AUGMENTS edges from old blob to new blob 5. Updates the semantic graph silently in the background @@ -469,8 +469,8 @@ AUGMENTS edges have a special format: ``` { - src_sha: "abc123...", // Old blob SHA - tgt_sha: "def456...", // New blob SHA + src_oid: "abc123...", // Old blob object ID + tgt_oid: "def456...", // New blob object ID rel_type: "AUGMENTS", confidence: 1.0, src_path: "core/src/old.c", // For readability @@ -656,11 +656,11 @@ Each link is stored as a Git commit in `refs/gitmind/edges/`. The commit ### Content Addressing -Links connect specific versions of files using their blob SHAs: +Links connect specific versions of files using their blob object IDs (OIDs): ``` -source_sha: 8a9f3b2c... # SHA of design.md's content -target_sha: 7c4d5e6f... # SHA of parser.c's content +source_oid: 8a9f3b2c... # Object ID of design.md's content +target_oid: 7c4d5e6f... # Object ID of parser.c's content ``` This means links point to exact versions. When files change, the AUGMENTS system (see `git-mind-install-hooks(1)`) tracks their evolution. @@ -879,7 +879,7 @@ $ git mind list - `dot` - Graphviz DOT format `--show-sha` -: Display blob SHAs (default: hide) +: Display blob object IDs (OIDs). Flag name keeps the legacy “SHA” label. `--show-augments` : Include AUGMENTS evolution edges @@ -1057,7 +1057,7 @@ dot -Tpng graph.dot -o graph.png Metadata may include: - `(confidence: 0.8)` - Less than certain connections -- `(SHA: abc123)` - When --show-sha is used +- `(OID: abc123)` - Shown when `--show-sha` is used (legacy flag name) - `(AUGMENTED)` - When file has evolved ### Following AUGMENTS @@ -1256,13 +1256,13 @@ Every semantic link is stored as a Git commit in refs/gitmind/edges/. Th For repositories with thousands of connections, git-mind builds Roaring Bitmap indices: - O(log N) query performance -- Sharded by SHA prefix +- Sharded by object ID (OID) prefix — CLI output may still say “SHA” - Stored in refs/gitmind/cache/ - Rebuilt on demand ### The Evolution System (AUGMENTS) -When files change, their blob SHAs change. The AUGMENTS system: +When files change, their blob object IDs change. The AUGMENTS system: - Tracks file evolution automatically - Preserves connections through changes @@ -1333,7 +1333,7 @@ $ git add core/src/parser.c && git commit -m "Optimize parser" # The connections followed the change! $ git mind list --from core/src/parser.c core/src/parser.c ──implements──> docs/parser-design.md -# (connection automatically updated to new blob SHA) +# (connection automatically updated to the new blob OID; CLI labels remain “SHA”) ``` ### Performance at Scale diff --git a/docs/cli/gitmind-cache-rebuild.md b/docs/cli/gitmind-cache-rebuild.md index 56429313..671bb3a7 100644 --- a/docs/cli/gitmind-cache-rebuild.md +++ b/docs/cli/gitmind-cache-rebuild.md @@ -48,10 +48,10 @@ Your semantic links live as CBOR-encoded Git commits in `refs/gitmind/edges/`, sharded into 256 buckets using the first byte of each SHA. +These are stored as Git tree objects under `refs/gitmind/cache/`, sharded into 256 buckets using the first byte of each OID. The CLI and on-disk layout may still refer to these prefixes as “SHA” while the tooling finishes renaming. ### The Magic: Roaring Bitmaps @@ -139,7 +139,7 @@ The cache automatically detects when it's stale by comparing the journal tip wit refs/gitmind/cache/main └── tree ├── meta.json # Cache metadata - ├── 00/ # SHA prefix shard + ├── 00/ # OID prefix shard │ ├── 00a1b2c3.forward │ └── 00a1b2c3.reverse ├── 01/ diff --git a/docs/cli/gitmind-install-hooks.md b/docs/cli/gitmind-install-hooks.md index c175d0b7..f499022e 100644 --- a/docs/cli/gitmind-install-hooks.md +++ b/docs/cli/gitmind-install-hooks.md @@ -41,11 +41,11 @@ The install-hooks command sets up a post-commit hook that automatically creates ## THE CIRCLE OF CHANGE -When you edit a file that has semantic links, those links point to a specific version (blob SHA). After your edit, the old links become outdated. The AUGMENTS system preserves the connection by creating evolution edges: +When you edit a file that has semantic links, those links point to a specific version (blob object ID, or OID). After your edit, the old links become outdated. The AUGMENTS system preserves the connection by creating evolution edges: ``` -Before edit: design.md ──implements──> main.c (SHA: abc123) -After edit: design.md ──implements──> main.c (SHA: def456) +Before edit: design.md ──implements──> main.c (OID: abc123) +After edit: design.md ──implements──> main.c (OID: def456) main.c:abc123 ──AUGMENTS──> main.c:def456 ``` @@ -121,7 +121,7 @@ fi After every commit, the hook: 1. Gets the list of modified files -2. For each file, finds its old blob SHA (before commit) +2. For each file, finds its old blob OID (before commit) 3. Searches recent edges (last 200) for links to that blob 4. Creates AUGMENTS edges from old blob to new blob 5. Updates the semantic graph silently in the background @@ -195,8 +195,8 @@ AUGMENTS edges have a special format: ``` { - src_sha: "abc123...", // Old blob SHA - tgt_sha: "def456...", // New blob SHA + src_oid: "abc123...", // Old blob object ID + tgt_oid: "def456...", // New blob object ID rel_type: "AUGMENTS", confidence: 1.0, src_path: "core/src/old.c", // For readability diff --git a/docs/cli/gitmind-link.md b/docs/cli/gitmind-link.md index ec0463c1..b38b125e 100644 --- a/docs/cli/gitmind-link.md +++ b/docs/cli/gitmind-link.md @@ -153,14 +153,14 @@ Each link is stored as a Git commit in `refs/gitmind/edges/`. The commit ### Content Addressing -Links connect specific versions of files using their blob SHAs: +Links connect specific versions of files using their Git object IDs (OIDs): ``` -source_sha: 8a9f3b2c... # SHA of design.md's content -target_sha: 7c4d5e6f... # SHA of parser.c's content +source_oid: 8a9f3b2c... # Object ID of design.md's content +target_oid: 7c4d5e6f... # Object ID of parser.c's content ``` -This means links point to exact versions. When files change, the AUGMENTS system (see `git-mind-install-hooks(1)`) tracks their evolution. +This means links point to exact versions. The CLI still exposes flags such as `--show-sha`; those names linger for compatibility but surface the same OID values. When files change, the AUGMENTS system (see `git-mind-install-hooks(1)`) tracks their evolution. ### Branch Isolation diff --git a/docs/cli/gitmind-list.md b/docs/cli/gitmind-list.md index c08f9d58..dba2a51c 100644 --- a/docs/cli/gitmind-list.md +++ b/docs/cli/gitmind-list.md @@ -91,7 +91,7 @@ $ git mind list - `dot` - Graphviz DOT format `--show-sha` -: Display blob SHAs (default: hide) +: Display blob object IDs (OIDs). The flag name retains the legacy “SHA” label. `--show-augments` : Include AUGMENTS evolution edges @@ -269,7 +269,7 @@ dot -Tpng graph.dot -o graph.png Metadata may include: - `(confidence: 0.8)` - Less than certain connections -- `(SHA: abc123)` - When --show-sha is used +- `(OID: abc123)` - Shown when `--show-sha` is used (legacy flag name) - `(AUGMENTED)` - When file has evolved ### Following AUGMENTS diff --git a/docs/cli/gitmind.md b/docs/cli/gitmind.md index b1807409..1eaa1de0 100644 --- a/docs/cli/gitmind.md +++ b/docs/cli/gitmind.md @@ -111,13 +111,13 @@ Every semantic link is stored as a Git commit in refs/gitmind/edges/. Th For repositories with thousands of connections, git-mind builds Roaring Bitmap indices: - O(log N) query performance -- Sharded by SHA prefix +- Sharded by object ID (OID) prefix — CLI output may still label these as “SHA” for compatibility - Stored in refs/gitmind/cache/ - Rebuilt on demand ### The Evolution System (AUGMENTS) -When files change, their blob SHAs change. The AUGMENTS system: +When files change, their blob object IDs (OIDs) change. The AUGMENTS system: - Tracks file evolution automatically - Preserves connections through changes @@ -188,7 +188,7 @@ $ git add core/src/parser.c && git commit -m "Optimize parser" # The connections followed the change! $ git mind list --from core/src/parser.c core/src/parser.c ──implements──> docs/parser-design.md -# (connection automatically updated to new blob SHA) +# (connection automatically updated to the new blob OID; CLI labels remain “SHA”) ``` ### Performance at Scale diff --git a/docs/features/Features_Ledger.md b/docs/features/Features_Ledger.md index afeea4f7..eb23b6d9 100644 --- a/docs/features/Features_Ledger.md +++ b/docs/features/Features_Ledger.md @@ -137,11 +137,11 @@ Minimum Viable Product - [ ] (GM.MVP.T001) Finalize strict equality -> [!INFO]- (1/4) ⣿⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀ — Step-by-step plan · 25% +> [!INFO]- (4/4) ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ — Step-by-step plan · 100% > - [x] Audit equality helpers in cache and journal to ensure OID-first comparisons never fall back when OIDs exist. -> - [ ] Add regression tests covering mixed OID/string paths across cache and journal utilities. -> - [ ] Update `docs/TECHNICAL.md` and any relevant ADRs to document strict equality behavior. -> - [ ] Sweep references to legacy SHA fallbacks in docs and CLI examples. +> - [x] Add regression tests covering mixed OID/string paths across cache and journal utilities. +> - [x] Update `docs/TECHNICAL.md` and any relevant ADRs to document strict equality behavior. +> - [x] Sweep references to legacy SHA fallbacks in docs and CLI examples. > [!NOTE]- Dependencies > - (none) diff --git a/meson.build b/meson.build index 72576fc8..b295c39c 100644 --- a/meson.build +++ b/meson.build @@ -318,6 +318,12 @@ test_cache_query = executable('test_cache_query', link_with : libgitmind, dependencies : [libsodium_dep, libgit2_dep, thread_dep]) +test_cache_strict_equality = executable('test_cache_strict_equality', + 'core/tests/unit/test_cache_strict_equality.c', + include_directories : inc, + link_with : libgitmind, + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) + test_cache_branch_limits = executable('test_cache_branch_limits', 'core/tests/unit/test_cache_branch_limits.c', include_directories : inc, @@ -384,6 +390,7 @@ test('cache_meta', test_cache_meta) test('cache_edge_map', test_cache_edge_map) test('journal_mixed_cbor', test_journal_mixed_cbor) test('cache_query', test_cache_query) +test('cache_strict_equality', test_cache_strict_equality) test('cache_branch_limits', test_cache_branch_limits) test('journal_safety', test_journal_safety) test('cache_shard_distribution', test_cache_shard_distribution) From e2eb59ca529c64cdec8ed52833a10659310ac4fb Mon Sep 17 00:00:00 2001 From: "P.R. PhiedBach" Date: Mon, 29 Sep 2025 14:43:41 +0000 Subject: [PATCH 3/6] opus(PR#174): seed rehearsal score (bc1fc319303bf1a00b43b03a40992ba788996b1a) --- ...c1fc319303bf1a00b43b03a40992ba788996b1a.md | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 docs/code-reviews/PR174/bc1fc319303bf1a00b43b03a40992ba788996b1a.md diff --git a/docs/code-reviews/PR174/bc1fc319303bf1a00b43b03a40992ba788996b1a.md b/docs/code-reviews/PR174/bc1fc319303bf1a00b43b03a40992ba788996b1a.md new file mode 100644 index 00000000..e9bbd424 --- /dev/null +++ b/docs/code-reviews/PR174/bc1fc319303bf1a00b43b03a40992ba788996b1a.md @@ -0,0 +1,110 @@ +--- +title: bc1fc319303bf1a00b43b03a40992ba788996b1a.md +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +--- + +# Code Review Feedback + +| Date | Agent | SHA | Branch | PR | +|------|-------|-----|--------|----| +| 2025-09-29 | CodeRabbit (and reviewers) | `bc1fc319303bf1a00b43b03a40992ba788996b1a` | [feat/next-hex-task](https://github.com/neuroglyph/git-mind/tree/feat/next-hex-task "neuroglyph/git-mind:feat/next-hex-task") | [PR#174](https://github.com/neuroglyph/git-mind/pull/174) | + +## CODE REVIEW FEEDBACK + +### General comment — coderabbitai[bot] + +```text + + + +> [!NOTE] +> Currently processing new changes in this PR. This may take a few minutes, please wait... +> +>
+> 📥 Commits +> +> Reviewing files that changed from the base of the PR and between e08d76fd9a93ff4f72a265e3b0c9327e26a06cc5 and bc1fc319303bf1a00b43b03a40992ba788996b1a. +> +>
+> +>
+> 📒 Files selected for processing (14) +> +> * `AGENTS.md` (1 hunks) +> * `core/tests/unit/test_cache_strict_equality.c` (1 hunks) +> * `core/tests/unit/test_journal_mixed_cbor.c` (3 hunks) +> * `docs/TECHNICAL.md` (4 hunks) +> * `docs/activity/2025-09.md` (1 hunks) +> * `docs/architecture/deprecations/legacy-sha-fields.md` (3 hunks) +> * `docs/cli/combined_markdown.md` (10 hunks) +> * `docs/cli/gitmind-cache-rebuild.md` (2 hunks) +> * `docs/cli/gitmind-install-hooks.md` (3 hunks) +> * `docs/cli/gitmind-link.md` (1 hunks) +> * `docs/cli/gitmind-list.md` (2 hunks) +> * `docs/cli/gitmind.md` (2 hunks) +> * `docs/features/Features_Ledger.md` (2 hunks) +> * `meson.build` (2 hunks) +> +>
+> +> ```ascii +> ________________________________________________________________________________________________________________________ +> < I've finally learned what 'upward compatible' means. It means we get to keep all our old mistakes. - Dennie van Tassel > +> ------------------------------------------------------------------------------------------------------------------------ +> \ +> \ (\__/) +> (•ㅅ•) +> /   づ +> ``` + + + + + + +
+✨ Finishing touches + +- [ ] 📝 Generate Docstrings +
+🧪 Generate unit tests + +- [ ] Create PR with unit tests +- [ ] Post copyable unit tests in a comment +- [ ] Commit unit tests in branch `feat/next-hex-task` + +
+ +
+ + + + + +--- + +Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. + +
+❤️ Share + +- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) +- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) +- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) +- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code) + +
+ +Comment `@coderabbitai help` to get the list of available commands and usage tips. + + +``` + +_Meta_: https://github.com/neuroglyph/git-mind/pull/174#issuecomment-3347395660 + +{response} + From 9b839ca3617b018dd970b785e650faed3fe15ca8 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 16:05:45 +0000 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`fea?= =?UTF-8?q?t/next-hex-task`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @flyingrobots. * https://github.com/neuroglyph/git-mind/pull/174#issuecomment-3347395660 The following files were modified: * `core/tests/unit/test_cache_strict_equality.c` * `core/tests/unit/test_journal_mixed_cbor.c` --- core/tests/unit/test_cache_strict_equality.c | 178 +++++++++++++++++++ core/tests/unit/test_journal_mixed_cbor.c | 24 +++ 2 files changed, 202 insertions(+) diff --git a/core/tests/unit/test_cache_strict_equality.c b/core/tests/unit/test_cache_strict_equality.c index f98673ae..4be865a7 100644 --- a/core/tests/unit/test_cache_strict_equality.c +++ b/core/tests/unit/test_cache_strict_equality.c @@ -25,6 +25,14 @@ typedef struct test_git_repo_port { gm_git_reference_tip_t glob_tip; } test_git_repo_port_t; +/** + * Provide the repository path for the requested kind (test stub). + * + * This stub does not implement path resolution and always returns an error + * indicating the operation is not implemented. + * + * @returns A gm_result_void_t error result containing `GM_ERR_NOT_IMPLEMENTED`. + */ static gm_result_void_t stub_repository_path(void *self, gm_git_repository_path_kind_t kind, char *out_buffer, @@ -36,6 +44,14 @@ static gm_result_void_t stub_repository_path(void *self, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Stub implementation for retrieving the current HEAD branch name used in tests. + * + * @param self Opaque pointer to the repository port instance (ignored). + * @param out_name Buffer to receive the branch name (ignored). + * @param out_name_size Size of the out_name buffer (ignored). + * @returns A gm_result_void_t representing the `GM_ERR_NOT_IMPLEMENTED` error. + */ static gm_result_void_t stub_head_branch(void *self, char *out_name, size_t out_name_size) { (void)self; @@ -44,6 +60,14 @@ static gm_result_void_t stub_head_branch(void *self, char *out_name, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Test stub for building a tree that always reports the operation as not implemented. + * + * This function does not construct or modify any tree and exists only for testing; + * it always fails with a "not implemented" error. + * + * @returns An error result with `GM_ERR_NOT_IMPLEMENTED`. + */ static gm_result_void_t stub_build_tree(void *self, const char *dir_path, gm_oid_t *out_tree_oid) { (void)self; @@ -52,6 +76,18 @@ static gm_result_void_t stub_build_tree(void *self, const char *dir_path, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Populate the output reference tip based on the provided reference name. + * + * If ref_name starts with GM_CACHE_REF_PREFIX, copies the stub's cache_tip to out_tip. + * If ref_name starts with GITMIND_EDGES_REF_PREFIX, copies the current journal tip + * (stub->journal_tips[stub->journal_tip_index]) to out_tip and advances journal_tip_index + * by one unless already at the last tip. For any other ref_name, sets out_tip to an all-zero tip. + * + * @param ref_name The reference name used to select which tip to return. + * @param out_tip Pointer to receive the selected or zeroed gm_git_reference_tip_t. + * @returns `GM_OK` on success; `GM_ERR_INVALID_ARGUMENT` if ref_name or out_tip is NULL. + */ static gm_result_void_t reference_tip(void *self, const char *ref_name, gm_git_reference_tip_t *out_tip) { test_git_repo_port_t *stub = (test_git_repo_port_t *)self; @@ -87,6 +123,17 @@ static gm_result_void_t reference_tip(void *self, const char *ref_name, return gm_ok_void(); } +/** + * Retrieve the latest reference tip matching a glob pattern from the test stub. + * + * The provided pattern is validated for non-NULL but not inspected; the function + * always returns the stub's preset glob_tip into `out_tip`. + * + * @param pattern Glob pattern to match (validated for non-NULL; otherwise ignored). + * @param out_tip Output pointer that receives the latest matching reference tip. + * @returns `gm_ok_void()` on success; `gm_err_void(GM_ERR_INVALID_ARGUMENT, ...)` + * if `pattern` or `out_tip` is NULL. + */ static gm_result_void_t reference_glob_latest(void *self, const char *pattern, gm_git_reference_tip_t *out_tip) { test_git_repo_port_t *stub = (test_git_repo_port_t *)self; @@ -99,6 +146,15 @@ static gm_result_void_t reference_glob_latest(void *self, const char *pattern, return gm_ok_void(); } +/** + * Stub implementation of reading a blob at a commit path that always fails. + * + * If provided, sets `*out_data` to NULL and `*out_size` to 0 before returning. + * + * @param out_data If non-NULL, receives NULL to indicate no data was produced. + * @param out_size If non-NULL, receives 0 to indicate zero bytes. + * @returns An error result indicating the operation is not implemented. + */ static gm_result_void_t stub_commit_read_blob(void *self, const gm_oid_t *commit_oid, const char *path, @@ -116,12 +172,25 @@ static gm_result_void_t stub_commit_read_blob(void *self, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Free memory backing a blob buffer. + * + * @param data Pointer to the blob buffer to free. + * @param size Size of the blob buffer in bytes (unused by this implementation). + */ static void blob_dispose(void *self, uint8_t *data, size_t size) { (void)self; (void)size; free(data); } +/** + * Stub implementation that attempts to read a commit message but always fails and clears the output pointer. + * + * @param commit_oid Commit OID to read; ignored by this test stub. + * @param out_message Pointer to receive the commit message; if non-NULL, set to NULL. + * @returns An error indicating the operation is not implemented (`GM_ERR_NOT_IMPLEMENTED`) wrapped in a `gm_result_void_t`. + */ static gm_result_void_t stub_commit_read_message(void *self, const gm_oid_t *commit_oid, char **out_message) { @@ -133,11 +202,21 @@ static gm_result_void_t stub_commit_read_message(void *self, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Free a heap-allocated commit message. + * + * @param message Pointer to a heap-allocated NUL-terminated commit message to free; may be NULL. + */ static void stub_commit_message_dispose(void *self, char *message) { (void)self; free(message); } +/** + * Test stub for walking commits that always reports the operation as not implemented. + * + * @returns An error result with `GM_ERR_NOT_IMPLEMENTED`. + */ static gm_result_void_t stub_walk_commits(void *self, const char *ref_name, gm_git_commit_visit_cb visit_callback, void *userdata) { @@ -148,6 +227,17 @@ static gm_result_void_t stub_walk_commits(void *self, const char *ref_name, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Stub implementation of commit tree size retrieval for tests. + * + * If `out_size_bytes` is non-NULL, it is set to 0. The function always + * returns an error result indicating the operation is not implemented. + * + * @param self Unused stub instance pointer. + * @param commit_oid Unused commit OID. + * @param out_size_bytes If non-NULL, receives 0. + * @returns A gm_result_void_t containing `GM_ERR_NOT_IMPLEMENTED`. + */ static gm_result_void_t stub_commit_tree_size(void *self, const gm_oid_t *commit_oid, uint64_t *out_size_bytes) { @@ -159,6 +249,16 @@ static gm_result_void_t stub_commit_tree_size(void *self, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Test stub for creating a commit in the repository port. + * + * This implementation does not create a commit and always returns a not-implemented error. + * + * @param self Pointer to the repository port instance (unused). + * @param spec Specification of the commit to create (unused). + * @param out_commit_oid Output OID for the created commit (unused). + * @returns A gm_result_void_t representing `GM_ERR_NOT_IMPLEMENTED`. + */ static gm_result_void_t stub_commit_create(void *self, const gm_git_commit_spec_t *spec, gm_oid_t *out_commit_oid) { @@ -168,6 +268,11 @@ static gm_result_void_t stub_commit_create(void *self, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Stub implementation of a reference update used by tests that always reports not implemented. + * + * @returns A `gm_result_void_t` representing an error with code `GM_ERR_NOT_IMPLEMENTED` and message "test stub". + */ static gm_result_void_t stub_reference_update( void *self, const gm_git_reference_update_spec_t *spec) { (void)self; @@ -175,6 +280,14 @@ static gm_result_void_t stub_reference_update( return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Stub that would resolve the blob OID for `path` at HEAD in test repository ports. + * + * @param self Pointer to the repository port instance (unused). + * @param path Path of the blob to resolve. + * @param out_blob_oid Output pointer to receive the blob OID when resolution succeeds. + * @returns A `gm_result_void_t` error value indicating not implemented (`GM_ERR_NOT_IMPLEMENTED`) for this test stub. + */ static gm_result_void_t stub_resolve_blob_at_head(void *self, const char *path, gm_oid_t *out_blob_oid) { (void)self; @@ -183,6 +296,11 @@ static gm_result_void_t stub_resolve_blob_at_head(void *self, const char *path, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Test stub that intentionally does not resolve a blob at a specific commit. + * + * @returns An error result with code GM_ERR_NOT_IMPLEMENTED indicating the operation is not implemented in the test stub. + */ static gm_result_void_t stub_resolve_blob_at_commit(void *self, const gm_oid_t *commit_oid, const char *path, @@ -194,6 +312,16 @@ static gm_result_void_t stub_resolve_blob_at_commit(void *self, return gm_err_void(GM_ERROR(GM_ERR_NOT_IMPLEMENTED, "test stub")); } +/** + * Stub that reports a commit has zero parents and indicates the operation is not implemented. + * + * If `out_parent_count` is non-NULL, it is set to 0 to indicate no parents. + * + * @param self Opaque repository port instance (unused). + * @param commit_oid OID of the commit to query (unused). + * @param out_parent_count Pointer to receive the parent count; set to 0 if non-NULL. + * @returns An error result indicating the function is not implemented (`GM_ERR_NOT_IMPLEMENTED`). + */ static gm_result_void_t stub_commit_parent_count(void *self, const gm_oid_t *commit_oid, size_t *out_parent_count) { @@ -224,17 +352,40 @@ static const gm_git_repository_port_vtbl_t TEST_PORT_VTBL = { .commit_parent_count = stub_commit_parent_count, }; +/** + * Prepare a test_git_repo_port_t for use by zeroing it and configuring its repository port vtable and self pointer. + * @param stub Pointer to the test stub to initialize. + */ static void init_stub(test_git_repo_port_t *stub) { gm_memset_safe(stub, sizeof(*stub), 0, sizeof(*stub)); stub->port.vtbl = &TEST_PORT_VTBL; stub->port.self = stub; } +/** + * Initialize `oid` from a hexadecimal string and abort on failure. + * + * @param oid Pointer to the OID to be populated. + * @param hex NUL-terminated hexadecimal string representation of the OID. + */ static void set_oid_from_hex(gm_oid_t *oid, const char *hex) { int rc = gm_oid_from_hex(oid, hex); assert(rc == GM_OK); } +/** + * Initialize a git reference tip structure with the given OID hex, commit time, and flags. + * + * Populates the provided gm_git_reference_tip_t by zeroing it and setting has_target and commit_time. + * If `zero_binary` is false and `hex` is non-NULL, the binary OID is parsed from `hex` and stored. + * If `hex` is non-NULL, its characters are copied into `tip->oid_hex` and NUL-terminated (truncated if necessary). + * + * @param tip Pointer to the tip structure to initialize. + * @param hex Hexadecimal string representation of the OID to store or copy; may be NULL. + * @param commit_time Commit timestamp to assign to the tip. + * @param has_target Whether the tip has a target reference. + * @param zero_binary If true, do not populate the binary OID even if `hex` is provided. + */ static void set_tip(gm_git_reference_tip_t *tip, const char *hex, uint64_t commit_time, bool has_target, bool zero_binary) { gm_memset_safe(tip, sizeof(*tip), 0, sizeof(*tip)); @@ -253,6 +404,16 @@ static void set_tip(gm_git_reference_tip_t *tip, const char *hex, } } +/** + * Fill a buffer with a repeated character and ensure null-termination. + * + * Writes `digit` into each of the first `buffer_len - 1` bytes and sets the + * final byte to the NUL terminator (`'\0'`). + * + * @param buffer Pointer to the destination buffer. + * @param buffer_len Total size of `buffer` in bytes; must be at least 1. + * @param digit Character to repeat throughout the buffer (except the terminator). + */ static void fill_hex(char *buffer, size_t buffer_len, char digit) { for (size_t i = 0; i + 1U < buffer_len; ++i) { buffer[i] = digit; @@ -260,10 +421,27 @@ static void fill_hex(char *buffer, size_t buffer_len, char digit) { buffer[buffer_len - 1U] = '\0'; } +/** + * Reset the journal tip sequence index to the beginning. + * + * This sets the test repository port's journal_tip_index to 0 so subsequent + * journal tip retrievals start from the first entry. + * + * @param stub Test repository port whose journal sequence will be reset. + */ static void reset_journal_sequence(test_git_repo_port_t *stub) { stub->journal_tip_index = 0U; } +/** + * Run unit tests that validate strict equality behavior of the cache check. + * + * Executes four scenarios that exercise gm_cache_is_stale with combinations of + * binary OID presence and legacy hex values to verify OID-first equality and + * hex-string fallback behavior. + * + * @returns 0 on success; a non-zero result (via failed assertion or abort) on failure. + */ int main(void) { printf("test_cache_strict_equality... "); diff --git a/core/tests/unit/test_journal_mixed_cbor.c b/core/tests/unit/test_journal_mixed_cbor.c index 9ac9fb12..b6d412e4 100644 --- a/core/tests/unit/test_journal_mixed_cbor.c +++ b/core/tests/unit/test_journal_mixed_cbor.c @@ -29,6 +29,16 @@ typedef struct { gm_edge_attributed_t last; } attr_ctx_t; +/** + * Capture a basic edge into a basic_ctx_t by copying the provided edge and + * incrementing the context's count. + * + * @param edge Pointer to the basic edge to capture. + * @param ud Pointer to a basic_ctx_t where the edge will be stored (last) + * and the count incremented. + * @returns `GM_OK` on success, `GM_ERR_INVALID_ARGUMENT` if `edge` or `ud` is NULL, + * or another `GM_*` error code propagated from the copy operation. + */ static int capture_basic_cb(const gm_edge_t *edge, void *ud) { if (edge == NULL || ud == NULL) { return GM_ERR_INVALID_ARGUMENT; @@ -43,6 +53,13 @@ static int capture_basic_cb(const gm_edge_t *edge, void *ud) { return GM_OK; } +/** + * Capture an attributed edge by copying it into the provided context and incrementing the count. + * + * @param edge Pointer to the attributed edge to capture; must not be NULL. + * @param ud Pointer to an attr_ctx_t that will receive the captured edge and have its count incremented; must not be NULL. + * @returns `GM_OK` on success, `GM_ERR_INVALID_ARGUMENT` if `edge` or `ud` is NULL, or an error code returned from the underlying copy operation. + */ static int capture_attr_cb(const gm_edge_attributed_t *edge, void *ud) { if (edge == NULL || ud == NULL) { return GM_ERR_INVALID_ARGUMENT; @@ -57,6 +74,13 @@ static int capture_attr_cb(const gm_edge_attributed_t *edge, void *ud) { return GM_OK; } +/** + * Run the mixed-CBOR journal integration test: create a temporary Git repository, + * write a commit containing one basic and one attributed CBOR-encoded edge payload, + * read them back, and verify their source/target OIDs match the generated values. + * + * @returns `0` on success, non-zero on failure. + */ int main(void) { printf("test_journal_mixed_cbor... "); git_libgit2_init(); From fff961fbe14ab977dd0e8b5e5101de4d808cb454 Mon Sep 17 00:00:00 2001 From: "P.R. PhiedBach" Date: Mon, 29 Sep 2025 16:05:59 +0000 Subject: [PATCH 5/6] opus(PR#175): seed rehearsal score (9b839ca3617b018dd970b785e650faed3fe15ca8) --- ...b839ca3617b018dd970b785e650faed3fe15ca8.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs/code-reviews/PR175/9b839ca3617b018dd970b785e650faed3fe15ca8.md diff --git a/docs/code-reviews/PR175/9b839ca3617b018dd970b785e650faed3fe15ca8.md b/docs/code-reviews/PR175/9b839ca3617b018dd970b785e650faed3fe15ca8.md new file mode 100644 index 00000000..e3fffbd2 --- /dev/null +++ b/docs/code-reviews/PR175/9b839ca3617b018dd970b785e650faed3fe15ca8.md @@ -0,0 +1,50 @@ +--- +title: 9b839ca3617b018dd970b785e650faed3fe15ca8.md +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +--- + +# Code Review Feedback + +| Date | Agent | SHA | Branch | PR | +|------|-------|-----|--------|----| +| 2025-09-29 | CodeRabbit (and reviewers) | `9b839ca3617b018dd970b785e650faed3fe15ca8` | [coderabbitai/docstrings/e2eb59c](https://github.com/neuroglyph/git-mind/tree/coderabbitai/docstrings/e2eb59c "neuroglyph/git-mind:coderabbitai/docstrings/e2eb59c") | [PR#175](https://github.com/neuroglyph/git-mind/pull/175) | + +## CODE REVIEW FEEDBACK + +### General comment — coderabbitai[bot] + +```text + + + +> [!IMPORTANT] +> ## Review skipped +> +> CodeRabbit bot authored PR detected. +> +> To trigger a single review, invoke the `@coderabbitai review` command. +> +> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file. + + + + + + +--- + + + +Comment `@coderabbitai help` to get the list of available commands and usage tips. + + +``` + +_Meta_: https://github.com/neuroglyph/git-mind/pull/175#issuecomment-3347845166 + +{response} + From 5f35f8e3e2988f9ee535527f3a28f4d2d326b556 Mon Sep 17 00:00:00 2001 From: "P.R. PhiedBach" Date: Mon, 29 Sep 2025 16:07:39 +0000 Subject: [PATCH 6/6] coda(PR#175): archive rehearsal notes --- ...b839ca3617b018dd970b785e650faed3fe15ca8.md | 50 ------------------- 1 file changed, 50 deletions(-) delete mode 100644 docs/code-reviews/PR175/9b839ca3617b018dd970b785e650faed3fe15ca8.md diff --git a/docs/code-reviews/PR175/9b839ca3617b018dd970b785e650faed3fe15ca8.md b/docs/code-reviews/PR175/9b839ca3617b018dd970b785e650faed3fe15ca8.md deleted file mode 100644 index e3fffbd2..00000000 --- a/docs/code-reviews/PR175/9b839ca3617b018dd970b785e650faed3fe15ca8.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: 9b839ca3617b018dd970b785e650faed3fe15ca8.md -description: Preserved review artifacts and rationale. -audience: [contributors] -domain: [quality] -tags: [review] -status: archive ---- - -# Code Review Feedback - -| Date | Agent | SHA | Branch | PR | -|------|-------|-----|--------|----| -| 2025-09-29 | CodeRabbit (and reviewers) | `9b839ca3617b018dd970b785e650faed3fe15ca8` | [coderabbitai/docstrings/e2eb59c](https://github.com/neuroglyph/git-mind/tree/coderabbitai/docstrings/e2eb59c "neuroglyph/git-mind:coderabbitai/docstrings/e2eb59c") | [PR#175](https://github.com/neuroglyph/git-mind/pull/175) | - -## CODE REVIEW FEEDBACK - -### General comment — coderabbitai[bot] - -```text - - - -> [!IMPORTANT] -> ## Review skipped -> -> CodeRabbit bot authored PR detected. -> -> To trigger a single review, invoke the `@coderabbitai review` command. -> -> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file. - - - - - - ---- - - - -Comment `@coderabbitai help` to get the list of available commands and usage tips. - - -``` - -_Meta_: https://github.com/neuroglyph/git-mind/pull/175#issuecomment-3347845166 - -{response} -