Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ca81a0f
refactor(core): Decouple cache and edge from libgit2 OID functions
flyingrobots Sep 28, 2025
203f4e3
chore: finalize coderabbit follow-up
flyingrobots Sep 28, 2025
5d58351
chore(review): seed feedback for PR #172 (203f4e3539b1d8fc73b641e94b0…
Sep 28, 2025
d550035
chore: allow worksheet deletions
flyingrobots Sep 28, 2025
7b7b5f5
Merge remote-tracking branch 'origin/chore/code-rabbit-cleanup' into …
flyingrobots Sep 28, 2025
6086275
chore(review): seed feedback for PR #172 (7b7b5f565ef23d959b126b0df50…
Sep 28, 2025
df33c8c
chore: automate review worksheet cleanup
flyingrobots Sep 28, 2025
5add14f
Merge remote-tracking branch 'origin/chore/code-rabbit-cleanup' into …
flyingrobots Sep 28, 2025
daca6b8
chore(review): seed feedback for PR #172 (5add14fcdabafff59a99ea484d6…
Sep 28, 2025
6370b30
chore: track unresolved coderabbit threads
flyingrobots Sep 28, 2025
8a32c6a
Merge remote-tracking branch 'origin/chore/code-rabbit-cleanup' into …
flyingrobots Sep 28, 2025
093b7cb
chore(review): seed feedback for PR #172 (8a32c6a810ebf632709cec6c928…
Sep 28, 2025
85ea6a6
docs: add phiedbach overture to PR172 worksheet
flyingrobots Sep 28, 2025
82dab21
Merge remote-tracking branch 'origin/chore/code-rabbit-cleanup' into …
flyingrobots Sep 28, 2025
a280499
chore(review): seed feedback for PR #172 (82dab217893b6ee8bb8dcdca221…
Sep 28, 2025
abea4c4
ci: give phiedbach a signature
flyingrobots Sep 28, 2025
b22c223
Merge remote-tracking branch 'origin/chore/code-rabbit-cleanup' into …
flyingrobots Sep 28, 2025
208b5f7
chore(review): seed feedback for PR #172 (b22c22398de39c4ab93e1c747b1…
Sep 28, 2025
fecc9d6
fix: address coderabbit feedback
flyingrobots Sep 28, 2025
25b4875
Merge remote-tracking branch 'origin/chore/code-rabbit-cleanup' into …
flyingrobots Sep 28, 2025
d11ee83
docs: sync latest worksheet seed
flyingrobots Sep 28, 2025
b2ed050
chore(review): seed feedback for PR #172 (d11ee830d5615e5582114d625a5…
Sep 28, 2025
5402cad
Merge branch 'main' into chore/code-rabbit-cleanup
flyingrobots Sep 29, 2025
0363808
chore(review): seed feedback for PR #172 (5402cadb5e7a3f71e0bee7a1e05…
Sep 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/auto-seed-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ jobs:
--pr "$PR" --out docs/code-reviews
- name: Commit and push
run: |
git config user.name "git-mind-bot"
git config user.email "gitmind-bot@example.com"
git config user.name "P.R. PhiedBach"
git config user.email "phiedbach@bikeshed.dungeon"
git add docs/code-reviews
if git diff --cached --quiet; then
echo "No changes to commit"; exit 0; fi
PR=${{ github.event.number }}
SHA=${{ github.event.pull_request.head.sha }}
git commit -m "chore(review): seed feedback for PR #$PR ($SHA)"
git commit -m "opus(PR#$PR): seed rehearsal score ($SHA)"
git push

seed-fork:
Expand Down Expand Up @@ -74,12 +74,12 @@ jobs:
python3 tools/review/seed_feedback_from_github.py \
--owner "${GITHUB_REPOSITORY%%/*}" --repo "${GITHUB_REPOSITORY##*/}" \
--pr "$PR" --out docs/code-reviews
git config user.name "git-mind-bot"
git config user.email "gitmind-bot@example.com"
git config user.name "P.R. PhiedBach"
git config user.email "phiedbach@bikeshed.dungeon"
git add docs/code-reviews
if git diff --cached --quiet; then
echo "No seeded changes to commit"; exit 0; fi
git commit -m "chore(review): seed feedback for PR #$PR"
git commit -m "opus(PR#$PR): seed rehearsal score"
# Attempt to push using GITMIND_PR_WORKSHEET_BOT if provided and contributor allows edits
if [ -n "$GITMIND_PR_WORKSHEET_BOT" ]; then
repo_full="${{ github.event.pull_request.head.repo.full_name }}"
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/coderabbit-status.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CodeRabbit Status

on:
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review]

permissions:
contents: read
pull-requests: read

jobs:
coderabbit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install requests
- name: Check unresolved CodeRabbit threads
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
python3 tools/review/check_coderabbit_threads.py \
--owner "${GITHUB_REPOSITORY%%/*}" \
--repo "${GITHUB_REPOSITORY##*/}" \
--pr ${{ github.event.number }}
48 changes: 48 additions & 0 deletions .github/workflows/review-artifact-cleanup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Cleanup Review Worksheets

on:
pull_request:
types: [closed]

permissions:
contents: write
pull-requests: write

jobs:
cleanup:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.base.ref }}

- name: Configure git
run: |
git config user.name "P.R. PhiedBach"
git config user.email "phiedbach@bikeshed.dungeon"

- name: Remove review artifacts
run: |
pr_number=${{ github.event.pull_request.number }}
review_dir="docs/code-reviews/PR${pr_number}"
if [ -d "$review_dir" ]; then
git rm -r "$review_dir"
fi
# Remove any rejected-suggestion docs tied to this PR number
git ls-files "docs/code-reviews/rejected-suggestions/*_${pr_number}_*.md" | xargs -r git rm

- name: Commit cleanup
run: |
if git diff --cached --quiet; then
echo "No review artifacts to delete; skipping commit."
exit 0
fi
git commit -m "coda(PR#${{ github.event.pull_request.number }}): archive rehearsal notes"

- name: Push cleanup commit
run: |
git push
Comment thread
coderabbitai[bot] marked this conversation as resolved.
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,20 @@ See archives under `docs/activity/` for older logs.
- CodeRabbit pass (batch 2): accepted output-zeroing and error-propagation notes—commit creation now clears result OIDs up front, commit message reads null-init outputs, hook blob lookups zero result buffers and distinguish NOT_FOUND from real failures, commit walks honour `GM_CALLBACK_STOP`, and the fake port header owns its std includes.
- CodeRabbit pass (batch 3): reaffirmed the wildebeest benchmark already routes queries via `gm_oid_t`, clarified detached-HEAD fallback inside `gm_hook_get_blob_sha`, and brought the PR171 review docs in line with the front-matter/TOC guidelines.

### 2025-09-28
- Hardened port usage: journal writer now bubbles explicit commit/ref update codes and retries fast-forward only once; cache rebuild composes a parent-aware commit spec and refuses non-fast-forward updates; libgit2 adapter enforces ancestry checks via `git_graph_descendant_of`.
- Filesystem polish: `gm_fs_path_normalize_logical`/`basename_append` zero outputs on failure paths, guard short buffers, and cap segment arrays; posix temp adapter wraps `_GNU_SOURCE` in an `#ifndef` so clang stays quiet.
- Fake git port + tests updated to use `gm_memset_safe`/`gm_strcpy_safe`, keeping parity with production safety helpers.
- CI status: `make ci-local` passes after the fast-forward guard, cache parent wiring, and path-utils fixes (clang-tidy clean).
- Outstanding follow-ups for future-me:
* Extract the duplicated `resolve_blob_identity` helper (edge + edge_attributed) into a shared utility so we maintain one implementation.
* Replace remaining `git_oid_tostr` usage with a core-level `gm_oid_to_hex` helper and drop the remaining direct libgit2 includes in cache/query paths.
* Sweep for any lingering `fprintf` debug traces before shipping.
* Run CodeRabbit worksheet checker again after touching docs to ensure no new “Decision:” gaps.
- Today’s continuation: pulled the trigger on that list—extracted `gm_edge_resolve_blob_identity` into `core/src/edge/internal/blob_identity.c`, introduced `gm_bytes_to_hex`/`gm_oid_to_hex`, and refactored cache rebuild/query to drop `git_oid_fmt/tostr`; `make ci-local` is green and `tools/review/check_worksheets.py docs/code-reviews/PR171/*.md` still flags the known undecided sections (see console), so next slot should be worksheet triage + CodeRabbit replies.
- Follow-up (same day): cleaned up CodeRabbit’s fake-port feedback—fake commit/update paths now surface `gm_strcpy_safe` truncations, the shared OID helpers carry project-compliant naming/includes, and the deleted worksheet directory is ready for a fresh seed if we decide to log post-merge notes.
- Quick-start when you return: rerun `make ci-local` if you touch the cache/journal surface, then tackle the shared blob helper → oid hex helper chain; once that’s in place, prep the PR summary referencing the fast-forward protections.

## Next Steps (handoff checklist)
- Complete on-disk cache migration to OID-only storage and naming; ensure rebuild and fallback readers handle both formats or gate with a one-time migration.
- Extend journal CBOR schema to store OIDs explicitly (not only derivable from legacy fields); update reader/writer and consumers.
Expand Down
4 changes: 2 additions & 2 deletions core/include/gitmind/cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ typedef struct {
uint32_t shard_bits; /**< Number of bits for sharding */
uint32_t version; /**< Cache format version */
char branch[GM_CACHE_BRANCH_NAME_SIZE]; /**< Branch name (may be truncated if too long) */
gm_oid_t journal_tip_oid_bin; /**< OID of last processed journal commit (preferred). Added at struct tail to preserve ABI layout. */
gm_oid_t cache_tip_oid; /**< OID of cache commit for this branch. */
gm_oid_t journal_tip_oid_bin; /**< OID of last processed journal commit (preferred). */
gm_oid_t cache_tip_oid; /**< OID of cache commit for this branch. Added at struct tail to preserve ABI layout. */
} gm_cache_meta_t;

/**
Expand Down
35 changes: 35 additions & 0 deletions core/include/gitmind/edge/internal/blob_identity.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */
/* © 2025 J. Kirby Ross / Neuroglyph Collective */

#ifndef GITMIND_EDGE_INTERNAL_BLOB_IDENTITY_H
#define GITMIND_EDGE_INTERNAL_BLOB_IDENTITY_H

#include <stdint.h>

#include "gitmind/context.h"
#include "gitmind/result.h"
#include "gitmind/types.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* Resolve the blob identity for a file path using the current repository head.
*
* On success, both the preferred OID (`out_oid`) and the legacy SHA-1 buffer
* (`legacy_sha`, sized to `GM_SHA1_SIZE`) are populated. When `GM_OID_RAWSZ`
* exceeds `GM_SHA1_SIZE`, the legacy buffer is zero-padded to maintain
* deterministic contents. Callers must provide non-null pointers.
Comment on lines +20 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix legacy buffer padding description.

The implementation zero-fills the legacy SHA buffer only when GM_OID_RAWSZ is smaller than GM_SHA1_SIZE. The doc currently says “exceeds,” so the guidance is inverted. Please update the phrasing so callers understand when padding happens.

- * (`legacy_sha`, sized to `GM_SHA1_SIZE`) are populated. When `GM_OID_RAWSZ`
- * exceeds `GM_SHA1_SIZE`, the legacy buffer is zero-padded to maintain
+ * (`legacy_sha`, sized to `GM_SHA1_SIZE`) are populated. When `GM_OID_RAWSZ`
+ * is smaller than `GM_SHA1_SIZE`, the legacy buffer is zero-padded to maintain
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* On success, both the preferred OID (`out_oid`) and the legacy SHA-1 buffer
* (`legacy_sha`, sized to `GM_SHA1_SIZE`) are populated. When `GM_OID_RAWSZ`
* exceeds `GM_SHA1_SIZE`, the legacy buffer is zero-padded to maintain
* deterministic contents. Callers must provide non-null pointers.
* On success, both the preferred OID (`out_oid`) and the legacy SHA-1 buffer
* (`legacy_sha`, sized to `GM_SHA1_SIZE`) are populated. When `GM_OID_RAWSZ`
* is smaller than `GM_SHA1_SIZE`, the legacy buffer is zero-padded to maintain
* deterministic contents. Callers must provide non-null pointers.
🤖 Prompt for AI Agents
In core/include/gitmind/edge/internal/blob_identity.h around lines 20 to 23, the
comment incorrectly states that the legacy SHA buffer is zero-padded when
GM_OID_RAWSZ "exceeds" GM_SHA1_SIZE; update the phrasing to say that the legacy
buffer is zero-padded when GM_OID_RAWSZ is smaller than GM_SHA1_SIZE (i.e., when
the preferred OID raw size is less than the legacy SHA-1 size), so callers know
padding happens to fill the remainder of the legacy buffer to GM_SHA1_SIZE;
ensure callers still know to provide non-null pointers.

*/
GM_NODISCARD gm_result_void_t gm_edge_resolve_blob_identity(
gm_context_t *ctx,
const char *path,
gm_oid_t *out_oid,
uint8_t *legacy_sha);

#ifdef __cplusplus
}
#endif

#endif /* GITMIND_EDGE_INTERNAL_BLOB_IDENTITY_H */
43 changes: 43 additions & 0 deletions core/include/gitmind/util/oid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */
/* © 2025 J. Kirby Ross / Neuroglyph Collective */

#ifndef GITMIND_UTIL_OID_H
#define GITMIND_UTIL_OID_H

#include <stddef.h>
#include <stdint.h>

#include "gitmind/result.h"
#include "gitmind/types.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* Convert raw bytes to a lowercase hexadecimal string.
*
* @param bytes Input byte array
* @param len Number of bytes to convert
* @param out Output buffer for the hex string
* @param out_size Size of the output buffer in bytes
* @return GM_OK on success, GM_ERR_INVALID_ARGUMENT or GM_ERR_BUFFER_TOO_SMALL otherwise
*/
GM_NODISCARD int gm_bytes_to_hex(const uint8_t *bytes, size_t len,
char *out, size_t out_size);

/**
* Convert a gm_oid_t into its hexadecimal string form.
*
* @param oid Pointer to the OID to encode
* @param out Output buffer for the hex string (including null terminator)
* @param out_size Size of the output buffer in bytes
* @return GM_OK on success, GM_ERR_INVALID_ARGUMENT or GM_ERR_BUFFER_TOO_SMALL otherwise
*/
GM_NODISCARD int gm_oid_to_hex(const gm_oid_t *oid, char *out, size_t out_size);

#ifdef __cplusplus
}
#endif

#endif /* GITMIND_UTIL_OID_H */
16 changes: 7 additions & 9 deletions core/src/app/cache/cache_rebuild_service.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

#include "gitmind/cache/internal/rebuild_service.h"

#include <git2/oid.h>

#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
Expand All @@ -29,6 +27,7 @@
#include "gitmind/security/memory.h"
#include "gitmind/security/string.h"
#include "gitmind/util/memory.h"
#include "gitmind/util/oid.h"
#include "gitmind/util/ref.h"

#define MAX_SHARD_PATH 32
Expand Down Expand Up @@ -121,8 +120,11 @@ static void get_oid_prefix(const gm_oid_t *oid, char *prefix, int bits) {
return;
}

char hex[GM_OID_HEX_CHARS];
git_oid_fmt(hex, oid); /* not null-terminated */
char hex[GM_OID_HEX_CHARS + 1] = {0};
if (gm_oid_to_hex(oid, hex, sizeof(hex)) != GM_OK) {
prefix[0] = '\0';
return;
}
if (chars > GM_OID_HEX_CHARS) {
chars = GM_OID_HEX_CHARS;
}
Expand All @@ -135,11 +137,7 @@ static void get_oid_prefix(const gm_oid_t *oid, char *prefix, int bits) {
}

static int oid_to_hex(const gm_oid_t *oid, char *out, size_t out_size) {
if (out_size < SHA_HEX_SIZE) {
return GM_ERR_INVALID_ARGUMENT;
}
(void)git_oid_tostr(out, out_size, oid);
return GM_OK;
return gm_oid_to_hex(oid, out, out_size);
}

typedef struct {
Expand Down
43 changes: 23 additions & 20 deletions core/src/cache/query.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "gitmind/security/string.h"
#include "gitmind/util/ref.h"
#include "gitmind/util/memory.h"
#include "gitmind/util/oid.h"
#include "gitmind/constants_internal.h"

/* Constants */
Expand All @@ -40,17 +41,6 @@ static int gm_hex_value(char c) {
return -1;
}

static void gm_raw_to_hex(const uint8_t *raw,
char out_hex[GM_OID_HEX_CHARS + 1]) {
static const char kHexDigits[] = "0123456789abcdef";
for (size_t i = 0; i < GM_OID_RAWSZ; ++i) {
unsigned byte = raw[i];
out_hex[i * 2] = kHexDigits[(byte >> 4) & 0xF];
out_hex[i * 2 + 1] = kHexDigits[byte & 0xF];
}
out_hex[GM_OID_HEX_CHARS] = '\0';
}

static int gm_hex_to_raw(const char *hex, uint8_t *out_raw, size_t raw_size) {
if (hex == NULL || out_raw == NULL) {
return GM_ERR_INVALID_ARGUMENT;
Expand Down Expand Up @@ -84,14 +74,25 @@ static bool gm_oid_equal(const gm_oid_t *a, const gm_oid_t *b) {
/* Get SHA prefix for sharding */
static void get_sha_prefix(const uint8_t *sha, char *prefix, int bits) {
int chars = (bits + 3) / BITS_PER_HEX_CHAR; /* Round up to hex chars */
if (chars <= 0) { prefix[0] = '\0'; return; }
git_oid tmp;
git_oid_fromraw(&tmp, sha);
char hex[GM_OID_HEX_CHARS];
git_oid_fmt(hex, &tmp); /* not null-terminated */
if (chars > GM_OID_HEX_CHARS) chars = GM_OID_HEX_CHARS;
/* Copy requested prefix and terminate */
for (int i = 0; i < chars; i++) prefix[i] = hex[i];
if (chars <= 0) {
prefix[0] = '\0';
return;
}

char hex[GM_OID_HEX_CHARS + 1] = {0};
if (gm_bytes_to_hex(sha, GM_OID_RAWSZ, hex, sizeof(hex)) != GM_OK) {
prefix[0] = '\0';
return;
}
if (chars > GM_OID_HEX_CHARS) {
chars = GM_OID_HEX_CHARS;
}
if (chars >= SHA_PREFIX_BUFFER_SIZE) {
chars = SHA_PREFIX_BUFFER_SIZE - 1;
}
for (int i = 0; i < chars; ++i) {
prefix[i] = hex[i];
}
prefix[chars] = '\0';
}

Expand Down Expand Up @@ -259,7 +260,9 @@ static int load_bitmap_from_cache(const gm_git_repository_port_t *port,
get_sha_prefix(sha, prefix, GM_CACHE_SHARD_BITS);

/* Convert SHA to hex */
gm_raw_to_hex(sha, sha_hex);
if (gm_bytes_to_hex(sha, GM_OID_RAWSZ, sha_hex, sizeof(sha_hex)) != GM_OK) {
return GM_ERR_BUFFER_TOO_SMALL;
}

/* Build path: prefix/sha.suffix */
{
Expand Down
Loading
Loading