diff --git a/.coderabbit.yml b/.coderabbit.yml new file mode 100644 index 00000000..168d6087 --- /dev/null +++ b/.coderabbit.yml @@ -0,0 +1,36 @@ +version: 1 + +review: + summary_preferred: true + max_total_comments: 50 + max_comments_per_file: 5 + prefer_patch_suggestions: true + tone: "concise, constructive, and action-oriented" + style: + - "Group similar issues together; avoid duplicates" + - "Reference rules/patterns (e.g., markdownlint MD041) over fragile line numbers" + - "Propose concrete fix-it snippets where possible" + - "Avoid inline comments on docs unless correctness is at stake; use summary" + +filters: + paths: + docs/**: summary_only + assets/**: ignore + build/**: ignore + .github/**: summary_only + +labels: + summary_only: + - docs-only + - chore + skip: + - vendored + +rules: + - name: "Front matter required" + paths: ["docs/**/*.md"] + summary: true + - name: "Single H1 per document" + paths: ["docs/**/*.md"] + summary: true + diff --git a/.githooks/pre-push b/.githooks/pre-push index 5f26dc45..d1e579b6 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -1,3 +1,56 @@ -#!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { printf >&2 "\n%s\n\n" "This repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'pre-push' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks')."; exit 2; } -git lfs pre-push "$@" +#!/usr/bin/env bash +set -euo pipefail + +# Guard: allow bypass +if [[ "${HOOKS_BYPASS:-}" == "1" ]]; then + exit 0 +fi + +remote_name=$1 +remote_url=$2 + +status=0 +while read -r local_ref local_sha remote_ref remote_sha; do + # Only enforce for pushes to main + if [[ "$remote_ref" == "refs/heads/main" ]]; then + # Determine the range to inspect + range="" + if [[ "$remote_sha" == "0000000000000000000000000000000000000000" ]]; then + # New branch/update; check last commit only + range="$local_sha^..$local_sha" + else + range="$remote_sha..$local_sha" + fi + + # Check if a changelog file is updated in this push + # Match common variants: CHANGELOG, CHANGES, with optional .md, case-insensitive, with or without paths + if ! git diff --name-only "$range" | grep -iqE '(^|.*/)(CHANGELOG|CHANGES)(\.md)?$'; then + echo "[pre-push] No changelog file updated for push to main ($range)." >&2 + echo " Expected one of: CHANGELOG.md, CHANGES.md, or variants (case-insensitive)." >&2 + echo " Run: make changelog-add m=\"\"" >&2 + echo " Or bypass once: HOOKS_BYPASS=1 git push" >&2 + status=1 + fi + fi + + # Worksheet gate: block pushes when review worksheets contain unfilled templates + # Determine the range to inspect for all branches + range="" + if [[ "$remote_sha" == "0000000000000000000000000000000000000000" ]]; then + range="$local_sha^..$local_sha" + else + range="$remote_sha..$local_sha" + fi + changed_files=$(git diff --name-only "$range" | grep '^docs/code-reviews/PR' || true) + if [[ -n "$changed_files" ]]; then + # Run worksheet checker + if ! python3 tools/review/check_worksheets.py $changed_files; then + echo "[pre-push] Unfilled review worksheet templates detected in changed files." >&2 + echo " Please complete Accepted/Rejected sections before pushing." >&2 + echo " Bypass once with HOOKS_BYPASS=1 if necessary." >&2 + status=1 + fi + fi +done + +exit $status diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..f4dc3d3d --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,4 @@ +docs-only: + - changed-files: + - any-glob-to-any-file: ['docs/**', 'README.md', 'AGENTS.md', 'CONTRIBUTING.md'] + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 841103ae..174538ae 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,28 +1,24 @@ -## Description +## Summary -Brief description of what this PR does. +Describe the change and why it’s needed. Link issues/PRDs if relevant. -## Related Issue +## Change Areas -Fixes #(issue number) +- [ ] Core (C) +- [ ] CLI / Hooks +- [ ] Docs +- [ ] CI / Tooling -## Type of Change +## Risk -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Documentation update +- [ ] Behavior change +- [ ] Public API change +- [ ] Docs-only -## Testing +## Code Review Guidance (for humans and CodeRabbit) -- [ ] I have added tests that prove my fix is effective or that my feature works -- [ ] New and existing unit tests pass locally with my changes -- [ ] I have added/updated documentation as needed +- Summarize top risks and propose concrete fix-it blocks where needed. +- Prefer rule/pattern references (e.g., markdownlint MD041) over fragile line numbers. +- Group nits into a brief summary; avoid inline comments on docs unless correctness is impacted. +- Provide patch suggestions for repeated issues. -## Checklist - -- [ ] My code follows the style guidelines of this project -- [ ] I have run `make test` in Docker -- [ ] I have made corresponding changes to the documentation -- [ ] My changes generate no new warnings -- [ ] I have added tests that prove my fix is effective or that my feature works diff --git a/.github/workflows/apply-feedback.yml b/.github/workflows/apply-feedback.yml new file mode 100644 index 00000000..2f6165c5 --- /dev/null +++ b/.github/workflows/apply-feedback.yml @@ -0,0 +1,36 @@ +name: Apply PR Feedback Replies + +on: + push: + branches-ignore: [ main ] + paths: + - 'docs/code-reviews/**.md' + +permissions: + contents: read + pull-requests: write + +jobs: + apply: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Detect changed review worksheets + id: files + run: | + set -e + BEFORE=${{ github.event.before }} + AFTER=${{ github.sha }} + git fetch --no-tags origin "$BEFORE" || true + CHANGED=$(git diff --name-only "$BEFORE" "$AFTER" | grep '^docs/code-reviews/PR' || true) + echo "changed=$CHANGED" >> $GITHUB_OUTPUT + - name: Apply feedback replies + if: steps.files.outputs.changed != '' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -e + OWNER=${GITHUB_REPOSITORY%%/*} + REPO=${GITHUB_REPOSITORY##*/} + python3 -m pip install --upgrade pip >/dev/null 2>&1 || true + python3 tools/review/apply_feedback_to_github.py --owner "$OWNER" --repo "$REPO" ${{ steps.files.outputs.changed }} diff --git a/.github/workflows/auto-seed-review.yml b/.github/workflows/auto-seed-review.yml new file mode 100644 index 00000000..c8b0d0fd --- /dev/null +++ b/.github/workflows/auto-seed-review.yml @@ -0,0 +1,123 @@ +name: Auto Seed Review Doc + +on: + pull_request_target: + types: [opened, reopened, synchronize] + +permissions: + contents: write + pull-requests: write + +jobs: + seed-same-repo: + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + steps: + - name: Checkout PR branch (same-repo) + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Seed feedback doc into repo + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR=${{ github.event.number }} + echo "Seeding review for PR#$PR (same-repo)" + python3 tools/review/seed_feedback_from_github.py \ + --owner "${GITHUB_REPOSITORY%%/*}" --repo "${GITHUB_REPOSITORY##*/}" \ + --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 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 push + + seed-fork: + if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + runs-on: ubuntu-latest + steps: + - name: Checkout fork branch (read-only) + uses: actions/checkout@v4 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Try to seed and push to fork (if maintainers can modify) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITMIND_PR_WORKSHEET_BOT: ${{ secrets.GITMIND_PR_WORKSHEET_BOT }} + run: | + set -e + PR=${{ github.event.number }} + echo "Seeding review for PR#$PR (fork)" + # Ensure our tools exist (checked out fork branch may not have them in older forks) + if [ ! -f tools/review/seed_feedback_from_github.py ]; then + echo "Tools missing in fork, checking out base repo tools..." + mkdir -p _tools + git clone --depth=1 https://github.com/${GITHUB_REPOSITORY}.git _tools/base + cp -r _tools/base/tools/review . || true + fi + 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 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" + # 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 }}" + branch="${{ github.event.pull_request.head.ref }}" + echo "Attempting push to fork $repo_full branch $branch" + git remote set-url origin https://x-access-token:$GITMIND_PR_WORKSHEET_BOT@github.com/$repo_full.git + if git push origin "HEAD:$branch"; then + echo "Pushed seeded doc to fork branch" + exit 0 + else + echo "Push to fork failed (likely edits not allowed); will upload artifact instead" + fi + else + echo "GITMIND_PR_WORKSHEET_BOT not set; cannot push to fork. Uploading artifact instead." + fi + # Fall through to artifact path + mkdir -p review-seed && cp -r docs/code-reviews/* review-seed/ || true + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: review-seed-PR${{ github.event.number }} + path: review-seed/** + - name: Comment with instructions + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request.number; + const msg = `I've generated a seeded review worksheet as an artifact for this PR (forked branch). + + Steps: + 1. Download the 'review-seed-PR${pr}' artifact from this workflow run. + 2. Copy the file under 'docs/code-reviews/PR${pr}/.md' into your branch. + 3. Fill in {response} sections with Accepted/Rejected templates and push. + + Maintainers: merging PR branches into the main repo will allow auto-commit of future seeds.`; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr, + body: msg + }); diff --git a/.github/workflows/label-docs-only.yml b/.github/workflows/label-docs-only.yml new file mode 100644 index 00000000..1e4b86a4 --- /dev/null +++ b/.github/workflows/label-docs-only.yml @@ -0,0 +1,18 @@ +name: Label docs-only PRs + +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + label: + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/labeler.yml diff --git a/.github/workflows/seed-review.yml b/.github/workflows/seed-review.yml new file mode 100644 index 00000000..f00a85b2 --- /dev/null +++ b/.github/workflows/seed-review.yml @@ -0,0 +1,32 @@ +name: Seed Review Doc (manual) +permissions: + contents: read + +on: + workflow_dispatch: + inputs: + pr: + description: PR number to seed + required: true + type: number + +jobs: + seed: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Run seeding script + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + python3 tools/review/seed_feedback_from_github.py \ + --owner ${GITHUB_REPOSITORY%%/*} --repo ${GITHUB_REPOSITORY##*/} --pr ${{ inputs.pr }} --out review-seed + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: review-seed + path: review-seed/** diff --git a/.github/workflows/update-features-ledger.yml b/.github/workflows/update-features-ledger.yml new file mode 100644 index 00000000..7a5c1677 --- /dev/null +++ b/.github/workflows/update-features-ledger.yml @@ -0,0 +1,32 @@ +name: Update Features Ledger + +on: + push: + branches: [ main ] + paths: + - 'docs/features/Features_Ledger.md' + - 'README.md' + - 'scripts/update_progress.py' + +permissions: + contents: write + +jobs: + update-ledger: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Update progress + run: | + python3 scripts/update_progress.py + - name: Commit changes if any + run: | + git config user.name "git-mind-bot" + git config user.email "gitmind-bot@example.com" + git add README.md docs/features/Features_Ledger.md + if git diff --cached --quiet; then + echo "No progress changes"; exit 0; fi + git commit -m "chore(ledger): auto-update features progress" + git push diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6652f66e..d115d5a1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,11 +26,32 @@ repos: entry: python3 tools/docs/check_docs.py --mode toc language: python files: ^docs/.*\.md$ + - id: docs-frontmatter-check + name: docs: frontmatter check + entry: python3 tools/docs/check_frontmatter.py + language: python + files: ^docs/.*\.md$ + - id: curate-agents + name: curate AGENTS.md recent developments + entry: python3 tools/agents/curate_recent.py + language: python + files: ^AGENTS\.md$ + - id: one-thing + name: policy: one-thing (1 file = 1 thing) + entry: python3 tools/quality/check_one_thing.py + language: python + pass_filenames: false + files: ^(include/|core/include/|core/src/).*(\.(c|h))$ - id: enforce-public-includes name: enforce public includes (no core/src) entry: bash tools/enforce-public-includes.sh language: system pass_filenames: false + - id: features-update + name: features ledger update + entry: python3 scripts/update_progress.py + language: python + files: ^docs/features/Features_Ledger\.md$ - repo: https://github.com/Yelp/detect-secrets rev: v1.4.0 hooks: diff --git a/AGENTS.md b/AGENTS.md index 2735aa36..1f6585b6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,76 @@ # Repository Guidelines +## Agent TL;DR (read me first) + +- Build/test: `make ci-local` (Docker-only; Meson guard prevents host builds). Docs verify: `make docs-verify`. +- Review seeding: `make seed-review PR=`; see `docs/tools/Review_Seeding.md`. +- Red lines: OID-first equality; use `gm_snprintf/gm_strcpy_safe`; append-only journal; no public ABI breaks (append fields). +- Refs via `gm_build_ref`; reject inputs starting with `refs/`. No `GM_PATH_MAX*2` buffers. +- Public headers are umbrella-safe with C++ linkage guards; run `make header-compile`. +- Docker-only policy: override host build only with `-Dforce_local_builds=true` when absolutely necessary. + +```yaml +agent: + docker_only: true + build: "make ci-local" + docs_verify: "make docs-verify" + seed_review: "make seed-review PR=${PR}" + header_compile: "make header-compile" + stop_signs: + - behavior_or_abi_change + - adding_dependencies + - changing_ci_gates + - large_refactor +``` + +## Boot Checklist + +- Check for nested `AGENTS.md` when editing in subdirs +- Use Docker for builds/tests; never host-run by default +- Run `make docs-verify` for doc changes; `make header-compile` for new public headers +- Respect One‑Thing rule for touched files +- For fork PRs, seeded worksheet may be artifact+comment (see Review_Seeding) +- Sync feature/milestone updates in `docs/features/Features_Ledger.md`; follow the Capture→GA lifecycle and rerun `make features-update`. + +## Recent Developments + +```yaml +recent_developments: + - date: 2025-09-16 + summary: > + Review seeding + auto‑replies: added seed script, auto‑seed workflow (same‑repo commit, fork artifact fallback), and apply‑feedback workflow that replies to PR comments from the worksheet. Renamed bot secret to GITMIND_PR_WORKSHEET_BOT. + - date: 2025-09-16 + summary: > + Worksheet gates: pre‑push guard + checker to block unfilled templates/undecided sections in review worksheets; bypass knob documented. + - date: 2025-09-16 + summary: > + Curated AGENTS.md: TL;DR + machine‑readable agent block + Boot Checklist; added Recent Developments YAML and auto‑archival to docs/activity via pre‑commit hook. + - date: 2025-09-16 + summary: > + Features Ledger: introduced docs/features/Features_Ledger.md + scripts/update_progress.py (KLoC‑weighted groups); added README features‑progress block; wired pre‑commit and a main‑branch auto‑update workflow; make features-update. + - date: 2025-09-16 + summary: > + Review ergonomics: CodeRabbit config (.coderabbit.yml) for summary‑first; PR template guidance; docs‑only auto‑labeling workflow. + - date: 2025-09-15 + summary: > + CI/CD: Fixed C-Core Gate failures. README now complies with markdownlint (underscore emphasis/strong, blockquote spacing, no inline HTML wrappers, code fence fixes, heading punctuation). Added local CI rig `tools/ci/ci_local.sh` and `make ci-local` to mirror C-Core Gate (markdownlint + docs checks, Dockerized build/tests/E2E, clang-tidy pass). + - date: 2025-09-15 + summary: > + Build: Switched Meson `c_std` from `c23` to `c2x` (Meson-compatible while targeting C23). Moved `option()`s to `meson_options.txt`. Set `GITMIND_DOCKER=1` in gauntlet Dockerfile to satisfy the Meson host-build guard inside gating containers. Registered new unit test target `test_ref_utils` in `meson.build`. + - date: 2025-09-15 + summary: > + Core (refs): `gm_build_ref` now accepts Git-style branch shorthands (slashes allowed), validates the combined ref via libgit2 `git_reference_normalize_name`, and rejects leading `refs/` to prevent double-prefixing. Updated header docs and added unit tests. + - date: 2025-09-15 + summary: > + Core (path safety): Removed `GM_PATH_MAX * 2` buffer in `core/src/cache/tree_builder.c`; now uses `GM_PATH_MAX` and bounded `gm_snprintf`. + - date: 2025-09-15 + summary: > + Docs: Added `docs/architecture/Ref_Name_Validation.md` (with ToC) documenting ref-building policy and validation. Updated `docs/CI_STRATEGY.md` and `docs/code-review/CLAUDE.md` to clarify C23 policy expressed via Meson `c2x`. Expanded `docs/operations/Environment_Variables.md` with `GITMIND_CI_IMAGE` and `HOOKS_BYPASS`. + - date: 2025-09-15 + summary: > + Reviews: Documented rejected suggestions for PR #169 under `docs/code-reviews/rejected-suggestions/` (canonical CHANGELOG filename, keep `md-verify` alias, ignore `.PHONY` ordering churn). Linked decisions in the PR thread. +``` + ## Project Structure & Module Organization - `core/` — C23 library: `include/` (public headers), `src/` (impl), `tests/` (unit). Add new code here. @@ -12,8 +83,23 @@ ## Build, Test, and Development Commands -- Configure + build: `meson setup build && ninja -C build` -- Run unit tests: `ninja -C build test` (or `meson test -C build`) +> [!WARNING] +> **DO NOT BUILD GITMIND OUTSIDE OF DOCKER** +> git-mind manipulates Git internals (refs/*, objects, config). Building or running tests on the host can corrupt this repository or others on your machine. All builds and tests must run inside the CI Docker image for safety and parity. + +> [!INFO] +> _If you really want to..._ +> Use the provided container workflow: +> - `make ci-local` — runs docs checks, builds, and unit tests in the CI image +> - `tools/ci/ci_local.sh` — same as above, invoked directly +> +> Advanced (at your own risk): +> - `meson setup build -Dforce_local_builds=true` — explicit Meson override +> - `GITMIND_ALLOW_HOST_BUILD=1` — legacy env override (discouraged) +> If you override, you accept responsibility for any repo damage. + +- Configure + build (inside CI container): `make ci-local` or `tools/ci/ci_local.sh` +- Run unit tests (inside CI container): done as part of `make ci-local` - Make shims: `make`, `make test`, `make clean` - Lint (CI parity): `./tools/docker-clang-tidy.sh` → produces `clang-tidy-report.txt` - Strict multi-compiler build: `./tools/gauntlet/run-gauntlet.sh` (recommended before PRs) @@ -54,7 +140,16 @@ - Make minimal, focused diffs; avoid drive-by refactors. Match existing patterns in `core/` and headers under `include/`. - Run build, tests, and lint locally before proposing changes. Keep changes zero-warnings and formatted. - - Prefer OID-first APIs for SHA-agnostic correctness. Use `git_oid` (typedef `gm_oid_t`) in new core interfaces, and compare via `git_oid_cmp`. +- Prefer OID-first APIs for SHA-agnostic correctness. Use `git_oid` (typedef `gm_oid_t`) in new core interfaces, and compare via `git_oid_cmp`. + +### One-Thing Rule (Touched code policy) + +- You touch it? You refactor it. If you modify a type or function that clearly bundles multiple concerns in a single file, split out the thing you touched into its own file (1 file = 1 thing): + - One enum per header. + - One struct per header (public structs under `include/gitmind/`, internal-only in `core/include/gitmind//internal/`). + - One responsibility per C file where practical. +- Scope: Only the item you touched (don’t boil the ocean). If you edit `Bar` in `foo.c` and `foo.c` contains 12 types, you only have to extract `Bar` now. +- Enforcement: Pre-commit includes a heuristic check (`one-thing`); bypass in emergencies with `GM_ONE_THING_ALLOW=1` in the environment. PRs should include a brief note when bypassed. ## Working Knowledge (for agents) @@ -69,13 +164,7 @@ ## Agent Activity Log -2025-09-14 -- CI/CD: Added Markdown linting with repo-aligned rules (`.markdownlint.jsonc`), `make md-lint`/`md-fix` targets, and path filters limiting core workflows to code paths. Enabled docker pull retry and set `GITMIND_SAFETY=off` for E2E in the core workflow. -- Header hygiene: Standardized header guards to `GITMIND_*`, added umbrella viability check (public headers compile standalone), and a header guard linter (`tools/quality/check_header_guards.py`) wired via Meson run target. -- OID-first migration: Introduced `typedef git_oid gm_oid_t` and `GM_OID_RAWSZ`. Migrated edges, cache queries, and hooks APIs to OID-first and switched equality to `git_oid_cmp`. Maintained compatibility where needed; CBOR decode backfills OIDs from legacy fields temporarily. -- Journal safety: Base64-encoded CBOR commit messages on write and decoded on read; added strict `gm_snprintf` overflow checks and security-conscious includes. -- CLI fixes: Adjusted `apps/cli` commands for include hygiene, error handling, and consistent porcelain output; freed libgit2 arrays correctly. -- Reviews/PRs: Pushed branches, resolved merge conflicts, and responded to CodeRabbit with summary feedback. Opened a PR for CodeRabbit + markdownlint config changes and updated PRs addressing invalid path errors and core cleanup. +See archives under `docs/activity/` for older logs. ## 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. @@ -97,18 +186,52 @@ If you maintain a separate experimental tracking system locally (e.g., a personal graph database or notes), keep it entirely outside this repository and CI. Do not include configuration or scripts here. -## Agent Activity Log - -- 2025-09-13 - - fix(include): guard time conversion macros in `include/gitmind/constants_internal.h`. - - fix(output): add missing EOF newlines in `include/gitmind/output.h` and `apps/cli/output.c`. - - feat(cli): introduce `gm_cli_ctx_t`; refactor `apps/cli/main.c` and commands to pass explicit CLI output context (removed global output). - - chore(cli): normalize includes; prefer specific public headers; added `inc_cli` include dirs in `meson.build` for CLI builds. - - feat(cli/safety): implement libgit2-based safety guard with strict official-repo match; add `GITMIND_SAFETY` override; extracted URL helper in `include/gitmind/safety.h`. - - test(core): add `core/tests/unit/test_safety.c` and register in Meson. - - ci(e2e): run E2E inside CI Docker image in `.github/workflows/c_core.yml`; improved `tests/e2e/run_all_tests.sh` to honor `GIT_MIND` env. - - fix(core/journal): add temporary CBOR decode wrappers in `core/src/journal/reader.c` (`gm_edge_decode_cbor_ex`, `gm_edge_attributed_decode_cbor_ex`) to unblock CLI until attributed decoder lands. - - fix(core/journal/tidy): replace `memset/strncpy/snprintf` in `reader.c` with safe wrappers (`GM_MEMSET_SAFE`, `gm_memcpy_safe`, `gm_snprintf`); include security headers. Updated `tools/docker-clang-tidy.sh` to set `GITMIND_DOCKER=1` for Meson. - - docs(ops): added `docs/operations/Environment_Variables.md`; linked from `README.md` and `docs/README.md`. Noted runtime safety behavior in Test Plan. - - style(include): use angle includes in `include/gitmind.h` and trim optional-include block comment. - - Opened PR: (branch `feat/cli-safety-e2e`). Built and ran unit tests in Docker (15/15 passing). E2E wired to run in CI container. +## Regression Guardrails + +- Build/run + - Docker-only for build and test; use `make ci-local` or `tools/ci/ci_local.sh`. + - Meson gate blocks host builds; override only with `-Dforce_local_builds=true` when necessary. +- API semantics + - Equality is OID-first: if both OIDs present they must match; SHA fallback only when OIDs are absent. + - Refs: build via `gm_build_ref`; reject inputs starting with `refs/`; follow ref-name safety rules. +- Safety ops + - Use `gm_snprintf`/`gm_strcpy_safe`/`gm_memcpy_span`; check return codes; treat truncation as error. + - Zero output buffers on error paths before formatting or copying. +- Public headers + - Umbrella-safe; direct includes only; add C++ linkage guards; stable header guards. +- Cache/journal + - Journal is append-only under `refs/gitmind/edges/`; cache under `refs/gitmind/cache/`. + - Avoid ABI breaks in public structs; append new fields at struct tail; mark deprecated fields. +- Paths/buffers + - Prefer `GM_PATH_MAX`; do not use `GM_PATH_MAX*2`; allocate if larger is required. +- Docs + - Front matter first; single H1; `## Table of Contents`; titles match H1; add `api_version` to API docs. + - License/SPDX comments after front matter. +- Tests + - Run in Docker; avoid HEAD assumptions in bare repos; seed branches in tests when needed. + +## PR Review Checklist + +- Scope & safety + - Docker-only build/test verified; no host-only assumptions. + - No public ABI breaks (or explicitly called out and versioned). +- Semantics & correctness + - OID-first behavior enforced; no SHA fallback when OIDs exist. + - Refs created via `gm_build_ref` with input validation. +- Security & robustness + - All string/memory ops use safe wrappers; truncation and errors handled. + - Outputs zeroed on failure; no partial writes. +- Headers & includes + - C++ linkage guards present; include-what-you-use; umbrella viability. +- Docs & tooling + - Front matter valid; titles/H1 aligned; TOC heading H2; API docs include `api_version`. + - Changelog updated for pushes to `main` (pre-push hook regex covers variants). +- CI & review hygiene + - Local CI (Docker) passes (tests + tidy as configured). + - If CodeRabbit runs: summary-first; propose fix-it patches; avoid noisy doc inlines. + +References +- Guide: `docs/tools/Review_Seeding.md` (end-to-end review seeding + bot setup) +- Code review seeding script: `tools/review/seed_feedback_from_github.py` (uses `GITHUB_TOKEN`). +- CodeRabbit config: `.coderabbit.yml` (summary-first, caps, doc filters). +- PR template: `.github/pull_request_template.md` (guidance for reviewers). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc833510..22da444f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,15 +4,34 @@ Thanks for your interest in improving git‑mind! This guide summarizes expectat ## Development Workflow -1. Build and test - - `meson setup build && ninja -C build` - - `ninja -C build test` +> [!WARNING] +> **DO NOT BUILD GITMIND OUTSIDE OF DOCKER** +> git-mind manipulates Git internals (refs/*, objects, config). Building or testing on the host can corrupt this repository or others on your machine. Always use the CI Docker image for safety and parity. + +> [!INFO] +> _If you really want to..._ +> Use the container workflow: +> - `make ci-local` — docs checks + build + unit tests in the CI image +> - `tools/ci/ci_local.sh` — same as above, direct +> Advanced (at your own risk): +> - `meson setup build -Dforce_local_builds=true` — explicit Meson override +> - `GITMIND_ALLOW_HOST_BUILD=1` — legacy env override (discouraged) + +1. Build and test (in Docker) + - `make ci-local` or `tools/ci/ci_local.sh` 2. Lint (CI parity) - `./tools/docker-clang-tidy.sh` → writes `clang-tidy-report.txt` - Header guards: `meson run -C build lint_header_guards` - Public headers compile: `ninja -C build header-compile` 3. Keep diffs focused; match existing patterns in `core/` and `include/`. +## Planning & Feature Tracking + +- `docs/features/Features_Ledger.md` is the single source of truth for roadmap, milestones, and tasks. +- Add new ideas under the appropriate feature group during **Capture**, tag them with a milestone, and log supporting tasks in the `## Tasklist` section. +- Progress bars are generated automatically—run `make features-update` after updating progress %, KLoC estimates, or tasks. +- Milestone callouts (`MVP`, `Alpha`, `Beta`, `v1.0.0`) auto-pull outstanding tasks based on `[Milestone]` tags; keep those tags in sync as work moves stages. + ## Code Style - Language: C23; warnings‑as‑errors; no VLAs or shadowing; explicit prototypes. @@ -42,8 +61,8 @@ Thanks for your interest in improving git‑mind! This guide summarizes expectat - Keep `.coderabbit.yml` updated. - Prefer summary reviews; limit per‑line comments on docs. - If rejecting a suggestion, document rationale under `docs/code-reviews/rejected-suggestions/` with links. +- See `docs/tools/Review_Seeding.md` for the auto‑worksheet flow and how to seed PR feedback locally or via CI. Seeded files live under `docs/code-reviews/PR/.md`. ## Getting Help Open a Discussion or Issue describing what you’re trying to do and where you’re blocked. Include your environment, commands run, and logs where possible. - diff --git a/Makefile b/Makefile index 9966e31f..f906ea64 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,73 @@ docker-clean: md-lint: @npx --yes -p markdownlint-cli2 markdownlint-cli2 "**/*.md" "#.git" "#build" + @python3 tools/docs/check_frontmatter.py + @python3 tools/docs/check_docs.py --mode link + @python3 tools/docs/check_docs.py --mode toc + +.PHONY: md-verify +md-verify: md-lint md-fix: @npx --yes -p markdownlint-cli2 markdownlint-cli2 --fix "**/*.md" "#.git" "#build" -.PHONY: all test clean docker-clean md-lint md-fix +# Changelog helpers +.PHONY: changelog-add +changelog-add: + @python3 tools/changelog/add_entry.py -m "$(m)" $(if $(n),-n "$(n)") + +.PHONY: ci-local +ci-local: + @bash tools/ci/ci_local.sh + +.PHONY: docs-verify +docs-verify: + @python3 tools/docs/check_frontmatter.py + @python3 tools/docs/check_docs.py --mode link + @python3 tools/docs/check_docs.py --mode toc + @python3 tools/docs/check_titles.py + +.PHONY: header-compile +header-compile: + @meson setup $(builddir) $(MESON_ARGS) >/dev/null 2>&1 || true + @ninja -C $(builddir) header-compile + +.PHONY: features-update +features-update: + @python3 scripts/update_progress.py + +.PHONY: seed-review +# Usage: make seed-review PR=169 [OWNER=neuroglyph REPO=git-mind] +seed-review: + @OWNER=$(OWNER); REPO=$(REPO); \ + if [ -z "$$OWNER" ] || [ -z "$$REPO" ]; then \ + url=$$(git remote get-url origin); \ + case "$$url" in \ + git@github.com:*) base=$${url#git@github.com:};; \ + https://github.com/*) base=$${url#https://github.com/};; \ + *) base="neuroglyph/git-mind";; \ + esac; \ + OWNER=$${base%%/*}; REPO=$${base##*/}; REPO=$${REPO%.git}; \ + fi; \ + if [ -z "$(PR)" ]; then echo "PR= is required"; exit 2; fi; \ + echo "Seeding review for $$OWNER/$$REPO PR $(PR)"; \ + python3 tools/review/seed_feedback_from_github.py --owner $$OWNER --repo $$REPO --pr $(PR) + +.PHONY: apply-feedback +# Usage: make apply-feedback FILES="docs/code-reviews/PR169/.md [more...]" [OWNER=neuroglyph REPO=git-mind] +apply-feedback: + @OWNER=$(OWNER); REPO=$(REPO); \ + if [ -z "$$OWNER" ] || [ -z "$$REPO" ]; then \ + url=$$(git remote get-url origin); \ + case "$$url" in \ + git@github.com:*) base=$${url#git@github.com:};; \ + https://github.com/*) base=$${url#https://github.com/};; \ + *) base="neuroglyph/git-mind";; \ + esac; \ + OWNER=$${base%%/*}; REPO=$${base##*/}; REPO=$${REPO%.git}; \ + fi; \ + if [ -z "$(FILES)" ]; then echo "FILES='' is required"; exit 2; fi; \ + echo "Applying feedback replies for $$OWNER/$$REPO"; \ + python3 tools/review/apply_feedback_to_github.py --owner $$OWNER --repo $$REPO $(FILES) + +.PHONY: all test clean docker-clean md-lint md-fix changelog-add ci-local diff --git a/README.md b/README.md index 1521ce73..753ccbe2 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,27 @@ # git-mind 🧠 > [!WARNING] -> **This project is in its early stages — _USE AT YOUR OWN RISK_.** -> Right now you can’t do much with it yet, and it *does* make changes inside your Git repo. +> __This project is in its early stages — _USE AT YOUR OWN RISK_.__ +> Right now you can’t do much with it yet, and it _does_ make changes inside your Git repo. > Proceed only if you’re curious and comfortable experimenting. -> -> This README is more of a **Vision Statement™** than a manual. It’s here to set the direction, hype the idea, and motivate ongoing work. -> -> If you’re intrigued, **star the repo** ⭐, follow along in **Discussions/Releases**, and watch it grow. -> -> — *flyingrobots* - -

+> +> This README is more of a __Vision Statement™__ than a manual. It’s here to set the direction, hype the idea, and motivate ongoing work. +> +> If you’re intrigued, __star the repo__ ⭐, follow along in __Discussions/Releases__, and watch it grow. +> +> — _flyingrobots_ + git-mind logo -

[![Build](https://img.shields.io/github/actions/workflow/status/neuroglyph/git-mind/ci.yml?branch=main)](https://github.com/neuroglyph/git-mind/actions) [![License](https://img.shields.io/badge/license-MIND--UCAL--1.0-blue)](LICENSE) [![Status](https://img.shields.io/badge/status-early--stage-orange)](docs/roadmap.md) -> **Version your thoughts. Branch your ideas. Merge understanding.** +> __Version your thoughts. Branch your ideas. Merge understanding.__ -`git-mind` transforms any Git repository into a **serverless knowledge graph**, where relationships evolve with your history and AI helps uncover hidden connections. +`git-mind` transforms any Git repository into a __serverless knowledge graph__, where relationships evolve with your history and AI helps uncover hidden connections. -**No servers. No setup. Just `git push` your mind.** +__No servers. No setup. Just `git push` your mind.__ --- @@ -34,9 +32,9 @@ ```bash # "What implements our authentication?" $ git mind query --implements "auth-flow.md" -src/auth.c (verified, 2 days ago) -tests/auth.test.js (verified, 2 days ago) -src/oauth.c (AI suggested, 1 hour ago) +core/src/auth.c (verified, 2 days ago) +tests/auth.test.c (verified, 2 days ago) +core/src/oauth.c (AI suggested, 1 hour ago) # See how your understanding evolved $ git checkout main~30 @@ -49,7 +47,7 @@ oauth2.c jwt_handler.c basic_auth.c # Your knowledge graph evolved with your code! -```` +``` ### Build a Living Knowledge Base @@ -84,15 +82,28 @@ Merging 47 new semantic edges... Install/build instructions: see `docs/install.md`. +> [!WARNING] +> **DO NOT BUILD GITMIND OUTSIDE OF DOCKER** +> git-mind manipulates Git internals (refs/*, objects, config). Building or running tests on the host can corrupt this repository or others on your machine. Always use the CI Docker image for safety and parity. + +> [!INFO] +> _If you really want to..._ +> Use the container workflow: +> - `make ci-local` — docs checks + build + unit tests in the CI image +> - `tools/ci/ci_local.sh` — same as above, direct +> Advanced (at your own risk): +> - `meson setup build -Dforce_local_builds=true` — explicit Meson override +> - `GITMIND_ALLOW_HOST_BUILD=1` — legacy env override (discouraged) + CLI examples (subset implemented today): ```bash # Start linking -git mind link README.md src/main.c --type documents -git mind link src/auth.c tests/auth.test.js --type "tested-by" +git mind link README.md core/src/main.c --type documents +git mind link core/src/auth.c tests/auth.test.c --type "tested-by" # Explore connections -git mind list --from src/auth.c +git mind list --from core/src/auth.c # Rebuild cache when needed git mind cache-rebuild @@ -106,28 +117,28 @@ Package managers (Homebrew/Winget): coming soon. ## ✨ The Magic -- **Your repo is the database** — No servers, no external dependencies -- **Time-travel built in** — Check out any commit, get the graph from that moment -- **AI copilot ready** — Let AI suggest connections, you review and merge -- **Branch and merge ideas** — Try wild connections in a branch, merge what works +- __Your repo is the database__ — No servers, no external dependencies +- __Time-travel built in__ — Check out any commit, get the graph from that moment +- __AI copilot ready__ — Let AI suggest connections, you review and merge +- __Branch and merge ideas__ — Try wild connections in a branch, merge what works --- ## 🔮 Real-World Uses -**Software Architecture** +__Software Architecture__ - Trace decisions through code - See which tests cover which requirements - Understand impact before refactoring -**Research & Writing** +__Research & Writing__ - Build Zettelkasten-style note networks - Track citation graphs - Fork and extend others' research -**Team Knowledge** +__Team Knowledge__ - Onboard developers with explorable codebases - Preserve institutional memory in the repo @@ -147,11 +158,11 @@ Found 23 potential connections... # You review and cherry-pick $ git mind review --source ai -src/cache.c implements docs/caching-strategy.md (confidence: 0.92) +core/src/cache.c implements docs/caching-strategy.md (confidence: 0.92) [Accept/Reject/Skip]? a # Clear attribution preserved -$ git mind list --from src/cache.c --format json +$ git mind list --from core/src/cache.c --format json { "to": "docs/caching-strategy.md", "type": "implements", @@ -173,7 +184,7 @@ $ git mind list --from src/cache.c --format json --- -## 🌱 Who's Using git-mind? +## 🌱 Who's Using git-mind - 🧪 Early adopters exploring Zettelkasten workflows in Git - 🔬 Researchers mapping papers → datasets → results @@ -186,6 +197,12 @@ Want to be featured? Open an issue and share your story. ## 📊 Status + +```text +█████████████████████████████░░░░░░░░░░░ 72% +``` + + 🚀 Early release — Core features work today, API may evolve 📅 Roadmap: `docs/roadmap.md` 🤝 Contributing: `CONTRIBUTING.md` @@ -209,11 +226,11 @@ The journey from idea to implementation taught us: Created by J. Kirby Ross and the Neuroglyph Collective. -*"Hakuna Matata"* — no worries, your semantic connections are safe. +_"Hakuna Matata"_ — no worries, your semantic connections are safe. ### THE PROMISE -*"Remember who you are."* +_"Remember who you are."_ Your code tells a story. `git-mind` helps you remember it, share it, and build upon it. @@ -223,17 +240,17 @@ In the great Circle of Development, no understanding is lost, no connection forg ## EPILOGUE -It was 3 AM. I had just finished implementing **Roaring Bitmaps**, and in my exhausted haze the wildebeest stampede from *The Lion King* suddenly snapped into place as the perfect metaphor. +It was 3 AM. I had just finished implementing __Roaring Bitmaps__, and in my exhausted haze the wildebeest stampede from _The Lion King_ suddenly snapped into place as the perfect metaphor. That moment of delirium became a spark. For a while, many of the `git-mind` docs had “Circle of Life” editions — Mufasa and Rafiki explaining caches, hooks, and semantic edges. Those playful docs are gone now, (well, not really, they're in the git history...),but the spirit remains. -Because sometimes **art makes itself.** +Because sometimes __art makes itself.__ --- -*"Oh yes, the past can hurt. But the way I see it, you can either run from it or learn from it."* +_"Oh yes, the past can hurt. But the way I see it, you can either run from it or learn from it."_ -**Choose to learn. Choose `git-mind`.** +__Choose to learn. Choose `git-mind`.__ --- diff --git a/apps/cli/list.md b/apps/cli/list.md index 93368108..0ee7926a 100644 --- a/apps/cli/list.md +++ b/apps/cli/list.md @@ -37,9 +37,9 @@ Why callbacks? ### Default Output ``` -IMPLEMENTS: src/main.c -> include/api.h +IMPLEMENTS: core/src/main.c -> include/api.h REFERENCES: README.md -> docs/design.md -DEPENDS_ON: Makefile -> src/main.c +DEPENDS_ON: Makefile -> core/src/main.c Total: 3 links ``` @@ -109,7 +109,7 @@ git-mind list --type implements git-mind list --since 2024-01-01 # Pattern matching -git-mind list "src/*.c" +git-mind list "core/src/*.c" # Combining filters git-mind list --type references docs/ --since yesterday @@ -207,21 +207,21 @@ REFERENCES: café.md -> naïve.md ### Current: Human-readable ``` -IMPLEMENTS: src/main.c -> include/api.h +IMPLEMENTS: core/src/main.c -> include/api.h ``` ### Future: Machine-readable ```bash $ git-mind list --format=json -{"type":"implements","src":"src/main.c","tgt":"include/api.h",...} +{"type":"implements","src":"core/src/main.c","tgt":"include/api.h",...} $ git-mind list --format=csv -implements,src/main.c,include/api.h,1.0,1720000000000 +implements,core/src/main.c,include/api.h,1.0,1720000000000 $ git-mind list --format=dot digraph G { - "src/main.c" -> "include/api.h" [label="implements"]; + "core/src/main.c" -> "include/api.h" [label="implements"]; } ``` @@ -230,8 +230,8 @@ digraph G { ```bash $ git-mind list --group-by=type IMPLEMENTS (5): - src/main.c -> include/api.h - src/parser.c -> include/parser.h + core/src/main.c -> include/api.h + core/src/parser.c -> include/parser.h ... REFERENCES (12): @@ -329,14 +329,14 @@ By type: augments: 17 (1%) Most connected: README.md (50 edges) - src/main.c (45 edges) + core/src/main.c (45 edges) ``` ### Watch Mode ```bash $ git-mind list --watch -IMPLEMENTS: src/main.c -> include/api.h +IMPLEMENTS: core/src/main.c -> include/api.h [watching for changes...] NEW: REFERENCES: README.md -> CHANGELOG.md ``` diff --git a/assets/images/git-mind-dag.svg b/assets/images/git-mind-dag.svg new file mode 100644 index 00000000..3333daf2 --- /dev/null +++ b/assets/images/git-mind-dag.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:05226ee74f8c9cece71c41e4a0568e61baacb64c098a7a8218260c572e93f389 +size 7508 diff --git a/core/include/gitmind/cache.h b/core/include/gitmind/cache.h index d7668ca0..2a24468c 100644 --- a/core/include/gitmind/cache.h +++ b/core/include/gitmind/cache.h @@ -10,6 +10,8 @@ #include #include +#include "gitmind/types.h" +#include "gitmind/result.h" #include "gitmind/context.h" @@ -30,7 +32,7 @@ extern "C" { #define GM_CACHE_SHARD_BITS 8 /* 2 hex chars = 256 shards */ #define GM_CACHE_REF_PREFIX "refs/gitmind/cache/" #define GM_CACHE_BRANCH_NAME_SIZE 64 /* Maximum branch name length */ -#define GM_CACHE_OID_STRING_SIZE (GIT_OID_HEXSZ + 1) /* OID hex + NUL */ +#define GM_CACHE_OID_STRING_SIZE (GM_OID_HEX_CHARS + 1) /* OID hex + NUL */ /* Cache result containing edge IDs matching a query */ typedef struct { @@ -42,19 +44,20 @@ typedef struct { /* Cache metadata and statistics */ typedef struct { uint64_t journal_tip_time; /**< Timestamp of last processed journal commit */ - char journal_tip_oid[GM_CACHE_OID_STRING_SIZE]; /**< SHA of last processed journal commit */ + char journal_tip_oid[GM_CACHE_OID_STRING_SIZE]; /**< @deprecated OID hex string of last processed journal commit */ uint64_t edge_count; /**< Total edges in cache */ uint64_t build_time_ms; /**< Time to build cache (milliseconds) */ uint32_t shard_bits; /**< Number of bits for sharding */ uint32_t version; /**< Cache format version */ - char branch[GM_CACHE_BRANCH_NAME_SIZE]; /**< Branch name */ + 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_cache_meta_t; /** * Initialize the cache subsystem * @return GM_OK on success, error code on failure */ -int gm_cache_init(void); +GM_NODISCARD int gm_cache_init(void); /** * Rebuild cache from journal data @@ -62,7 +65,7 @@ int gm_cache_init(void); * @param force_full Force full rebuild instead of incremental * @return GM_OK on success, error code on failure */ -int gm_cache_rebuild(gm_context_t *ctx, const char *branch, bool force_full); +GM_NODISCARD int gm_cache_rebuild(gm_context_t *ctx, const char *branch, bool force_full); /** * Query edges by source OID (forward traversal) @@ -71,7 +74,7 @@ int gm_cache_rebuild(gm_context_t *ctx, const char *branch, bool force_full); * @param result Output result structure * @return GM_OK on success, error code on failure */ -int gm_cache_query_fanout(gm_context_t *ctx, const char *branch, const gm_oid_t *src_oid, +GM_NODISCARD int gm_cache_query_fanout(gm_context_t *ctx, const char *branch, const gm_oid_t *src_oid, gm_cache_result_t *result); /** @@ -82,7 +85,7 @@ int gm_cache_query_fanout(gm_context_t *ctx, const char *branch, const gm_oid_t * @param result Output result structure * @return GM_OK on success, error code on failure */ -int gm_cache_query_fanin(gm_context_t *ctx, const char *branch, const gm_oid_t *tgt_oid, +GM_NODISCARD int gm_cache_query_fanin(gm_context_t *ctx, const char *branch, const gm_oid_t *tgt_oid, gm_cache_result_t *result); /** @@ -92,7 +95,7 @@ int gm_cache_query_fanin(gm_context_t *ctx, const char *branch, const gm_oid_t * * @param meta Output metadata structure * @return GM_OK on success, error code on failure */ -int gm_cache_load_meta(gm_context_t *ctx, const char *branch, gm_cache_meta_t *meta); +GM_NODISCARD int gm_cache_load_meta(gm_context_t *ctx, const char *branch, gm_cache_meta_t *meta); /** * Check if cache needs rebuilding @@ -100,7 +103,7 @@ int gm_cache_load_meta(gm_context_t *ctx, const char *branch, gm_cache_meta_t *m * @param branch Branch name to check cache for * @return true if cache is stale and needs rebuild */ -bool gm_cache_is_stale(gm_context_t *ctx, const char *branch); +GM_NODISCARD bool gm_cache_is_stale(gm_context_t *ctx, const char *branch); /** * Get cache statistics @@ -109,7 +112,7 @@ bool gm_cache_is_stale(gm_context_t *ctx, const char *branch); * @param cache_size_bytes Output cache size in bytes * @return GM_OK on success, error code on failure */ -int gm_cache_stats(gm_context_t *ctx, const char *branch, uint64_t *edge_count, +GM_NODISCARD int gm_cache_stats(gm_context_t *ctx, const char *branch, uint64_t *edge_count, uint64_t *cache_size_bytes); /** diff --git a/core/include/gitmind/cbor/cbor.h b/core/include/gitmind/cbor/cbor.h index fdaea118..449e4f1e 100644 --- a/core/include/gitmind/cbor/cbor.h +++ b/core/include/gitmind/cbor/cbor.h @@ -23,7 +23,7 @@ GM_RESULT_DEF(gm_result_uint64, uint64_t); * @param max_size Maximum buffer size to prevent overruns * @return Result containing the uint64_t value or error */ -gm_result_uint64_t gm_cbor_read_uint(const uint8_t *buf, size_t *offset, size_t max_size); +GM_NODISCARD gm_result_uint64_t gm_cbor_read_uint(const uint8_t *buf, size_t *offset, size_t max_size); /** * Read a CBOR byte string from buffer. @@ -34,7 +34,7 @@ gm_result_uint64_t gm_cbor_read_uint(const uint8_t *buf, size_t *offset, size_t * @param expected_len Expected length of byte string * @return Result indicating success or error */ -gm_result_void_t gm_cbor_read_bytes(const uint8_t *buf, size_t *offset, size_t max_size, +GM_NODISCARD gm_result_void_t gm_cbor_read_bytes(const uint8_t *buf, size_t *offset, size_t max_size, uint8_t *data, size_t expected_len); /** @@ -46,7 +46,7 @@ gm_result_void_t gm_cbor_read_bytes(const uint8_t *buf, size_t *offset, size_t m * @param max_text_len Maximum text buffer size * @return Result indicating success or error */ -gm_result_void_t gm_cbor_read_text(const uint8_t *buf, size_t *offset, size_t max_size, +GM_NODISCARD gm_result_void_t gm_cbor_read_text(const uint8_t *buf, size_t *offset, size_t max_size, char *text, size_t max_text_len); /** @@ -60,7 +60,7 @@ gm_result_void_t gm_cbor_read_text(const uint8_t *buf, size_t *offset, size_t ma * @param buf_size Size of output buffer * @return Result containing number of bytes written or error */ -gm_result_size_t gm_cbor_write_uint(uint64_t value, uint8_t *buf, size_t buf_size); +GM_NODISCARD gm_result_size_t gm_cbor_write_uint(uint64_t value, uint8_t *buf, size_t buf_size); /** * Write a CBOR byte string to buffer. @@ -70,7 +70,7 @@ gm_result_size_t gm_cbor_write_uint(uint64_t value, uint8_t *buf, size_t buf_siz * @param data_len Length of bytes to write * @return Result containing number of bytes written or error */ -gm_result_size_t gm_cbor_write_bytes(uint8_t *buf, size_t buf_size, +GM_NODISCARD gm_result_size_t gm_cbor_write_bytes(uint8_t *buf, size_t buf_size, const uint8_t *data, size_t data_len); /** @@ -80,6 +80,6 @@ gm_result_size_t gm_cbor_write_bytes(uint8_t *buf, size_t buf_size, * @param text Null-terminated text to write * @return Result containing number of bytes written or error */ -gm_result_size_t gm_cbor_write_text(uint8_t *buf, size_t buf_size, const char *text); +GM_NODISCARD gm_result_size_t gm_cbor_write_text(uint8_t *buf, size_t buf_size, const char *text); #endif /* GITMIND_CBOR_CBOR_H */ diff --git a/core/include/gitmind/constants.h b/core/include/gitmind/constants.h deleted file mode 100644 index 1b0db979..00000000 --- a/core/include/gitmind/constants.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */ -/* © 2025 J. Kirby Ross / Neuroglyph Collective */ - -#ifndef GITMIND_CONSTANTS_H -#define GITMIND_CONSTANTS_H - -/** - * @file constants.h - * @brief String and numeric constants for git-mind - */ - -/* String constants for relationship types */ -#define GM_STR_IMPLEMENTS "implements" -#define GM_STR_REFERENCES "references" -#define GM_STR_DEPENDS_ON "depends_on" -#define GM_STR_AUGMENTS "augments" -#define GM_STR_CUSTOM "custom" - -/* Environment variable values for source types */ -#define GM_ENV_VAL_HUMAN "human" -#define GM_ENV_VAL_CLAUDE "claude" -#define GM_ENV_VAL_GPT "gpt" -#define GM_ENV_VAL_SYSTEM "system" - -/* Confidence range constants */ -#define GM_CONFIDENCE_MIN 0.0f -#define GM_CONFIDENCE_MAX 1.0f - -/* Format buffer sizes */ -#define GM_FORMAT_BUFFER_SIZE 512 - -/* Cache system constants */ -#define BITS_PER_HEX_CHAR 4 -#define HEX_CHARS_PER_BYTE 2 -#define SHA_HEX_SIZE (GM_OID_RAWSZ * HEX_CHARS_PER_BYTE + 1) -#define ZERO_SHA_STRING "0000000000000000000000000000000000000000" -#define EDGE_MAP_BUCKETS 1024 -#define DIR_PERMS_NORMAL 0755 -#define REF_NAME_BUFFER_SIZE 256 -#define RM_COMMAND_EXTRA_SIZE 10 -#define CACHE_SIZE_ESTIMATE_PER_EDGE 32 - -/* Legacy error constants (for compatibility) */ -#define GM_NOT_FOUND GM_ERR_NOT_FOUND -#define GM_NO_MEMORY GM_ERR_OUT_OF_MEMORY -#define GM_IO_ERROR GM_ERR_IO_FAILED -#define GM_INVALID_ARG GM_ERR_INVALID_ARGUMENT - -#endif /* GITMIND_CONSTANTS_H */ diff --git a/core/include/gitmind/edge.h b/core/include/gitmind/edge.h index 721cab35..387d0d5f 100644 --- a/core/include/gitmind/edge.h +++ b/core/include/gitmind/edge.h @@ -103,6 +103,9 @@ gm_result_void_t gm_edge_encode_cbor(const gm_edge_t *edge, uint8_t *buffer, */ gm_result_edge_t gm_edge_decode_cbor(const uint8_t *buffer, size_t len); +/* Internal extension: decode and report bytes consumed */ +int gm_edge_decode_cbor_ex(const uint8_t *buffer, size_t len, gm_edge_t *edge, size_t *consumed); + #ifdef __cplusplus } #endif diff --git a/core/include/gitmind/edge_attributed.h b/core/include/gitmind/edge_attributed.h index e5ef08b4..0295e87a 100644 --- a/core/include/gitmind/edge_attributed.h +++ b/core/include/gitmind/edge_attributed.h @@ -23,15 +23,6 @@ extern "C" { * author information, source type (human/AI), and confidence scores. */ -/** - * Lane types for edge organization - */ -typedef enum { - GM_LANE_PRIMARY = 0, /**< Primary knowledge lane */ - GM_LANE_EXPLORATION = 1,/**< Exploratory connections */ - GM_LANE_REVIEW = 2, /**< Under review */ - GM_LANE_ARCHIVED = 3 /**< Archived/deprecated */ -} gm_lane_type_t; /** * Attributed edge structure with full metadata diff --git a/core/include/gitmind/journal.h b/core/include/gitmind/journal.h index 008d9a01..0829cb4e 100644 --- a/core/include/gitmind/journal.h +++ b/core/include/gitmind/journal.h @@ -4,6 +4,7 @@ #ifndef GITMIND_JOURNAL_H #define GITMIND_JOURNAL_H +#include "gitmind/result.h" #include "gitmind/edge.h" #include "gitmind/edge_attributed.h" #include "gitmind/context.h" @@ -45,7 +46,7 @@ typedef int (*gm_journal_read_attributed_callback_t)(const gm_edge_attributed_t * @param userdata User data passed to callback * @return 0 on success, error code on failure */ -int gm_journal_read(gm_context_t *ctx, const char *branch, +GM_NODISCARD int gm_journal_read(gm_context_t *ctx, const char *branch, gm_journal_read_callback_t callback, void *userdata); /** @@ -57,7 +58,7 @@ int gm_journal_read(gm_context_t *ctx, const char *branch, * @param userdata User data passed to callback * @return 0 on success, error code on failure */ -int gm_journal_read_attributed(gm_context_t *ctx, const char *branch, +GM_NODISCARD int gm_journal_read_attributed(gm_context_t *ctx, const char *branch, gm_journal_read_attributed_callback_t callback, void *userdata); /** @@ -68,7 +69,7 @@ int gm_journal_read_attributed(gm_context_t *ctx, const char *branch, * @param count Number of edges * @return 0 on success, error code on failure */ -int gm_journal_append(gm_context_t *ctx, const gm_edge_t *edges, size_t count); +GM_NODISCARD int gm_journal_append(gm_context_t *ctx, const gm_edge_t *edges, size_t count); /** * Append attributed edges to journal @@ -78,7 +79,7 @@ int gm_journal_append(gm_context_t *ctx, const gm_edge_t *edges, size_t count); * @param count Number of edges * @return 0 on success, error code on failure */ -int gm_journal_append_attributed(gm_context_t *ctx, const gm_edge_attributed_t *edges, size_t count); +GM_NODISCARD int gm_journal_append_attributed(gm_context_t *ctx, const gm_edge_attributed_t *edges, size_t count); /** * Create a Git commit with journal data @@ -89,7 +90,7 @@ int gm_journal_append_attributed(gm_context_t *ctx, const gm_edge_attributed_t * * @param len Length of data * @return 0 on success, error code on failure */ -int gm_journal_create_commit(gm_context_t *ctx, const char *ref, const void *data, size_t len); +GM_NODISCARD int gm_journal_create_commit(gm_context_t *ctx, const char *ref, const void *data, size_t len); #ifdef __cplusplus } diff --git a/core/include/gitmind/result.h b/core/include/gitmind/result.h index 04e1be31..b7fab6cd 100644 --- a/core/include/gitmind/result.h +++ b/core/include/gitmind/result.h @@ -43,61 +43,72 @@ GM_RESULT_DEF(gm_result_u64, uint64_t); /* Legacy name for compatibility during migration */ typedef gm_result_ptr_t gm_result_t; +/* Attributes */ +#if defined(__has_c_attribute) +# if __has_c_attribute(nodiscard) +# define GM_NODISCARD [[nodiscard]] +# else +# define GM_NODISCARD +# endif +#else +# define GM_NODISCARD +#endif + /* Success constructors */ -static inline gm_result_void_t gm_ok_void(void) { +GM_NODISCARD static inline gm_result_void_t gm_ok_void(void) { return (gm_result_void_t){.ok = true, .u.val = 0}; } -static inline gm_result_ptr_t gm_ok_ptr(void *val) { +GM_NODISCARD static inline gm_result_ptr_t gm_ok_ptr(void *val) { return (gm_result_ptr_t){.ok = true, .u.val = val}; } -static inline gm_result_int_t gm_ok_int(int val) { +GM_NODISCARD static inline gm_result_int_t gm_ok_int(int val) { return (gm_result_int_t){.ok = true, .u.val = val}; } -static inline gm_result_bool_t gm_ok_bool(bool val) { +GM_NODISCARD static inline gm_result_bool_t gm_ok_bool(bool val) { return (gm_result_bool_t){.ok = true, .u.val = val}; } -static inline gm_result_size_t gm_ok_size(size_t val) { +GM_NODISCARD static inline gm_result_size_t gm_ok_size(size_t val) { return (gm_result_size_t){.ok = true, .u.val = val}; } -static inline gm_result_u32_t gm_ok_u32(uint32_t val) { +GM_NODISCARD static inline gm_result_u32_t gm_ok_u32(uint32_t val) { return (gm_result_u32_t){.ok = true, .u.val = val}; } /* Error constructors */ -static inline gm_result_void_t gm_err_void(gm_error_t *err) { +GM_NODISCARD static inline gm_result_void_t gm_err_void(gm_error_t *err) { return (gm_result_void_t){.ok = false, .u.err = err}; } -static inline gm_result_ptr_t gm_err_ptr(gm_error_t *err) { +GM_NODISCARD static inline gm_result_ptr_t gm_err_ptr(gm_error_t *err) { return (gm_result_ptr_t){.ok = false, .u.err = err}; } -static inline gm_result_int_t gm_err_int(gm_error_t *err) { +GM_NODISCARD static inline gm_result_int_t gm_err_int(gm_error_t *err) { return (gm_result_int_t){.ok = false, .u.err = err}; } -static inline gm_result_bool_t gm_err_bool(gm_error_t *err) { +GM_NODISCARD static inline gm_result_bool_t gm_err_bool(gm_error_t *err) { return (gm_result_bool_t){.ok = false, .u.err = err}; } -static inline gm_result_size_t gm_err_size(gm_error_t *err) { +GM_NODISCARD static inline gm_result_size_t gm_err_size(gm_error_t *err) { return (gm_result_size_t){.ok = false, .u.err = err}; } -static inline gm_result_u32_t gm_err_u32(gm_error_t *err) { +GM_NODISCARD static inline gm_result_u32_t gm_err_u32(gm_error_t *err) { return (gm_result_u32_t){.ok = false, .u.err = err}; } -static inline gm_result_u64_t gm_ok_u64(uint64_t val) { +GM_NODISCARD static inline gm_result_u64_t gm_ok_u64(uint64_t val) { return (gm_result_u64_t){.ok = true, .u.val = val}; } -static inline gm_result_u64_t gm_err_u64(gm_error_t *err) { +GM_NODISCARD static inline gm_result_u64_t gm_err_u64(gm_error_t *err) { return (gm_result_u64_t){.ok = false, .u.err = err}; } diff --git a/core/include/gitmind/types.h b/core/include/gitmind/types.h index b0b8f5f5..14bc7f75 100644 --- a/core/include/gitmind/types.h +++ b/core/include/gitmind/types.h @@ -6,6 +6,7 @@ #include #include +/* Pull in libgit2 OID for public gm_oid_t definition */ #include #ifdef __cplusplus @@ -24,7 +25,12 @@ extern "C" { #define GIT_OID_RAWSZ 20 #endif #define GM_OID_RAWSZ GIT_OID_RAWSZ -#define GM_PATH_MAX 256 +#ifndef GIT_OID_HEXSZ +#define GM_OID_HEX_CHARS 40 +#else +#define GM_OID_HEX_CHARS GIT_OID_HEXSZ +#endif +#define GM_PATH_MAX 4096 #define GM_ULID_SIZE 26 #define GM_FORMAT_BUFFER_SIZE 512 diff --git a/core/include/gitmind/util/ref.h b/core/include/gitmind/util/ref.h new file mode 100644 index 00000000..3afd338e --- /dev/null +++ b/core/include/gitmind/util/ref.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */ +/* © 2025 J. Kirby Ross / Neuroglyph Collective */ + +#ifndef GITMIND_UTIL_REF_H +#define GITMIND_UTIL_REF_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Build a full Git ref name with bounds checking. + * out/out_sz: destination buffer and size + * prefix : ref prefix (e.g., "refs/gitmind/edges/") + * branch : Git-style shorthand ref (may include '/'; must NOT start with "refs/") + * Validation uses libgit2's git_reference_normalize_name on the combined ref. + * Returns GM_OK or error code. + */ +int gm_build_ref(char *out, size_t out_sz, const char *prefix, + const char *branch); + +#ifdef __cplusplus +} +#endif + +#endif /* GITMIND_UTIL_REF_H */ diff --git a/core/src/cache/bitmap.c b/core/src/cache/bitmap.c index 84731427..2a9ddb99 100644 --- a/core/src/cache/bitmap.c +++ b/core/src/cache/bitmap.c @@ -52,7 +52,7 @@ int gm_bitmap_serialize(const gm_bitmap_t *bitmap, uint8_t **buffer, /* Allocate buffer */ *buffer = malloc(total_size); if (!*buffer) { - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; } /* Write header - buffer is malloc'd so alignment is guaranteed */ @@ -122,20 +122,20 @@ int gm_bitmap_write_file(const gm_bitmap_t *bitmap, const char *path) { FILE *file = fopen(path, "wb"); if (!file) { free(buffer); - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } size_t written = fwrite(buffer, 1, size, file); int save_errno = errno; if (fclose(file) != 0) { free(buffer); - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } free(buffer); if (written != size) { errno = save_errno; - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } return GM_OK; @@ -144,22 +144,22 @@ int gm_bitmap_write_file(const gm_bitmap_t *bitmap, const char *path) { int gm_bitmap_read_file(const char *path, gm_bitmap_ptr *bitmap) { FILE *file = fopen(path, "rb"); if (!file) { - return GM_NOT_FOUND; + return GM_ERR_NOT_FOUND; } /* Get file size */ if (fseek(file, 0, SEEK_END) != 0) { fclose(file); - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } long file_size = ftell(file); if (file_size < 0) { fclose(file); - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } if (fseek(file, 0, SEEK_SET) != 0) { fclose(file); - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } if (file_size < 0 || file_size > INT32_MAX) { @@ -173,18 +173,18 @@ int gm_bitmap_read_file(const char *path, gm_bitmap_ptr *bitmap) { uint8_t *buffer = malloc(buffer_size); if (!buffer) { fclose(file); - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; } size_t read = fread(buffer, 1, buffer_size, file); if (fclose(file) != 0) { free(buffer); - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } if (read != buffer_size) { free(buffer); - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } /* Deserialize */ diff --git a/core/src/cache/builder.c b/core/src/cache/builder.c index 607e764c..8ac361a0 100644 --- a/core/src/cache/builder.c +++ b/core/src/cache/builder.c @@ -25,12 +25,16 @@ #include "gitmind/cache/bitmap.h" #include "cache_internal.h" #include "gitmind/constants.h" +#include "gitmind/constants_internal.h" #include "gitmind/context.h" #include "gitmind/edge.h" #include "gitmind/error.h" #include "gitmind/journal.h" #include "gitmind/types.h" +#include "gitmind/security/memory.h" #include "gitmind/security/string.h" +#include "gitmind/util/ref.h" +#include "gitmind/util/memory.h" /* Local constants */ #define CACHE_TEMP_DIR "/tmp/gitmind-cache-\x58\x58\x58\x58\x58\x58" @@ -67,14 +71,20 @@ static edge_map_t *edge_map_create(size_t size) { return map; } -/* Hash function for SHA */ +/* Hash function for OID: lightweight mixing for better distribution */ static size_t oid_hash(const gm_oid_t *oid, size_t size) { - size_t hash = 0; - const uint8_t *raw = git_oid_raw(oid); + const uint8_t *raw = (const uint8_t *)oid->id; + uint32_t x = 0x9E3779B9u; /* golden ratio */ for (int i = 0; i < GM_OID_RAWSZ; i++) { - hash = (hash * OID_HASH_MULTIPLIER) + raw[i]; + x ^= raw[i]; + x *= 0x85EBCA6Bu; /* mix */ + x ^= (x >> 13); } - return hash % size; + /* final avalanche */ + x ^= x >> 16; + x *= 0x7FEB352Du; + x ^= x >> 15; + return (size_t)(x % (uint32_t)size); } /* Add edge ID to map */ @@ -94,13 +104,13 @@ static int edge_map_add(edge_map_t *map, const gm_oid_t *oid, uint32_t edge_id) /* Create new entry */ entry = calloc(1, sizeof(edge_map_entry_t)); if (!entry) - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; entry->oid = *oid; entry->bitmap = gm_bitmap_create(); if (!entry->bitmap) { free(entry); - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; } gm_bitmap_add(entry->bitmap, edge_id); @@ -131,19 +141,17 @@ static void edge_map_free(edge_map_t *map) { /* Get SHA prefix for sharding */ static void get_oid_prefix(const gm_oid_t *oid, char *prefix, int bits) { - size_t bytes = (size_t)((bits + 7) / 8); - size_t offset = 0; - const uint8_t *raw = git_oid_raw(oid); - for (size_t i = 0; i < bytes; i++) { - (void)gm_snprintf(prefix + offset, (size_t)MAX_SHARD_PATH - offset, - "%02x", raw[i]); - offset += HEX_CHARS_PER_BYTE; - } - prefix[offset] = '\0'; + int chars = (bits + 3) / BITS_PER_HEX_CHAR; /* Round up to hex chars */ + if (chars <= 0) { prefix[0] = '\0'; return; } + char hex[GM_OID_HEX_CHARS]; + git_oid_fmt(hex, oid); /* not null-terminated */ + if (chars > GM_OID_HEX_CHARS) chars = GM_OID_HEX_CHARS; + for (int i = 0; i < chars && i < MAX_SHARD_PATH - 1; i++) prefix[i] = hex[i]; + prefix[(chars < (MAX_SHARD_PATH - 1)) ? chars : (MAX_SHARD_PATH - 1)] = '\0'; } static int oid_to_hex(const gm_oid_t *oid, char *out, size_t out_size) { - if (out_size < SHA_HEX_SIZE) return GM_INVALID_ARG; + if (out_size < SHA_HEX_SIZE) return GM_ERR_INVALID_ARGUMENT; (void)git_oid_tostr(out, out_size, oid); return GM_OK; } @@ -250,7 +258,7 @@ static int cache_prepare_rebuild(gm_context_t *ctx, /* Create temp directory */ (void)gm_snprintf(temp_dir, GM_PATH_MAX, "%s", CACHE_TEMP_DIR); if (!mkdtemp(temp_dir)) { - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } return GM_OK; @@ -277,7 +285,7 @@ static int cache_build_edge_map(edge_map_t **forward, edge_map_t **reverse) { if (!*forward || !*reverse) { edge_map_free(*forward); edge_map_free(*reverse); - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; } return GM_OK; @@ -299,10 +307,10 @@ static int cache_get_journal_tip(git_repository *repo, const char *branch, git_reference *journal_ref = NULL; char journal_ref_name[REF_NAME_BUFFER_SIZE]; { - int rn = gm_snprintf(journal_ref_name, sizeof(journal_ref_name), - "refs/gitmind/edges/%s", branch); - if (rn < 0 || (size_t)rn >= sizeof(journal_ref_name)) { - return GM_ERR_BUFFER_TOO_SMALL; + int brc = gm_build_ref(journal_ref_name, sizeof(journal_ref_name), + GITMIND_EDGES_REF_PREFIX, branch); + if (brc != GM_OK) { + return brc; } } @@ -311,6 +319,7 @@ static int cache_get_journal_tip(git_repository *repo, const char *branch, if (tip_oid) { git_oid_tostr(meta->journal_tip_oid, sizeof(meta->journal_tip_oid), tip_oid); + meta->journal_tip_oid_bin = *tip_oid; /* Also get the timestamp of the tip commit */ git_commit *tip_commit = NULL; @@ -322,7 +331,8 @@ static int cache_get_journal_tip(git_repository *repo, const char *branch, git_reference_free(journal_ref); } else { /* No journal yet */ - strcpy(meta->journal_tip_oid, ZERO_SHA_STRING); + meta->journal_tip_oid[0] = '\0'; + gm_memset_safe(&meta->journal_tip_oid_bin, sizeof(meta->journal_tip_oid_bin), 0, sizeof(meta->journal_tip_oid_bin)); } return GM_OK; @@ -377,9 +387,9 @@ static int cache_update_ref(git_repository *repo, const char *branch, const git_oid *commit_oid) { char ref_name[REF_NAME_BUFFER_SIZE]; { - int rn = gm_snprintf(ref_name, sizeof(ref_name), "%s%s", GM_CACHE_REF_PREFIX, branch); - if (rn < 0 || (size_t)rn >= sizeof(ref_name)) { - return GM_ERR_BUFFER_TOO_SMALL; + int brc = gm_build_ref(ref_name, sizeof(ref_name), GM_CACHE_REF_PREFIX, branch); + if (brc != GM_OK) { + return brc; } } @@ -456,7 +466,11 @@ static int gm_cache_rebuild_internal(git_repository *repo, const char *branch, meta.build_time_ms = (uint64_t)((clock() - start_time) / CLOCKS_PER_MS); meta.shard_bits = GM_CACHE_SHARD_BITS; meta.version = GM_CACHE_VERSION; - strncpy(meta.branch, branch, GM_CACHE_BRANCH_NAME_SIZE - 1); + if (strlen(branch) >= GM_CACHE_BRANCH_NAME_SIZE) { + rc = GM_ERR_INVALID_ARGUMENT; + goto cleanup; + } + (void)gm_strcpy_safe(meta.branch, GM_CACHE_BRANCH_NAME_SIZE, branch); /* Get journal tip info */ rc = cache_get_journal_tip(repo, branch, &meta); @@ -479,7 +493,7 @@ static int gm_cache_rebuild_internal(git_repository *repo, const char *branch, /* Public cache rebuild function matching cache.h signature */ int gm_cache_rebuild(gm_context_t *ctx, const char *branch, bool force_full) { if (!ctx || !ctx->git_repo || !branch) { - return GM_INVALID_ARG; + return GM_ERR_INVALID_ARGUMENT; } return gm_cache_rebuild_internal((git_repository *)ctx->git_repo, branch, force_full); diff --git a/core/src/cache/query.c b/core/src/cache/query.c index 6912bf91..2de8535e 100644 --- a/core/src/cache/query.c +++ b/core/src/cache/query.c @@ -24,6 +24,9 @@ #include "gitmind/journal.h" #include "gitmind/security/memory.h" #include "gitmind/security/string.h" +#include "gitmind/util/ref.h" +#include "gitmind/util/memory.h" +#include "gitmind/constants_internal.h" /* Constants */ #define CACHE_MAX_AGE_SECONDS 3600 /* 1 hour */ @@ -35,12 +38,15 @@ /* 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 */ - for (int i = 0; i < chars; i++) { - size_t off = (size_t)i * (size_t)HEX_CHARS_PER_BYTE; - (void)gm_snprintf(prefix + off, (size_t)HEX_CHARS_PER_BYTE + 1, "%02x", - sha[i]); - } - prefix[(size_t)chars * (size_t)HEX_CHARS_PER_BYTE] = '\0'; + 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]; + prefix[chars] = '\0'; } /* Load cache metadata from commit message */ @@ -50,14 +56,14 @@ static int find_legacy_cache_ref(git_repository *repo, const char *branch, git_o { int rn = gm_snprintf(pattern, sizeof(pattern), "%s%s/*", GM_CACHE_REF_PREFIX, branch); if (rn < 0 || (size_t)rn >= sizeof(pattern)) { - return GM_NOT_FOUND; + return GM_ERR_NOT_FOUND; } } git_reference *best_ref = NULL; git_reference *ref = NULL; git_reference_iterator *iter = NULL; int rc = git_reference_iterator_glob_new(&iter, repo, pattern); - if (rc < 0) return GM_NOT_FOUND; + if (rc < 0) return GM_ERR_NOT_FOUND; uint64_t best_time = 0; while (git_reference_next(&ref, iter) == 0) { @@ -82,14 +88,14 @@ static int find_legacy_cache_ref(git_repository *repo, const char *branch, git_o } git_reference_iterator_free(iter); if (ref) git_reference_free(ref); - if (!best_ref) return GM_NOT_FOUND; + if (!best_ref) return GM_ERR_NOT_FOUND; if (out_time) *out_time = best_time; git_reference_free(best_ref); return GM_OK; } int gm_cache_load_meta(gm_context_t *ctx, const char *branch, gm_cache_meta_t *meta) { - if (!ctx || !ctx->git_repo || !branch || !meta) return GM_INVALID_ARG; + if (!ctx || !ctx->git_repo || !branch || !meta) return GM_ERR_INVALID_ARGUMENT; git_repository *repo = (git_repository *)ctx->git_repo; git_reference *ref = NULL; git_commit *commit = NULL; @@ -98,9 +104,9 @@ int gm_cache_load_meta(gm_context_t *ctx, const char *branch, gm_cache_meta_t *m /* Build cache ref name and resolve */ { - int rn = gm_snprintf(ref_name, sizeof(ref_name), "%s%s", GM_CACHE_REF_PREFIX, branch); - if (rn < 0 || (size_t)rn >= sizeof(ref_name)) { - return GM_ERR_BUFFER_TOO_SMALL; + int brc = gm_build_ref(ref_name, sizeof(ref_name), GM_CACHE_REF_PREFIX, branch); + if (brc != GM_OK) { + return brc; } } rc = git_reference_lookup(&ref, repo, ref_name); @@ -108,12 +114,12 @@ int gm_cache_load_meta(gm_context_t *ctx, const char *branch, gm_cache_meta_t *m if (rc == 0) { rc = git_reference_name_to_id(&oid, repo, ref_name); git_reference_free(ref); - if (rc < 0) return GM_NOT_FOUND; + if (rc < 0) return GM_ERR_NOT_FOUND; } else { /* Fallback: look for legacy timestamped refs */ uint64_t ts = 0; rc = find_legacy_cache_ref(repo, branch, &oid, &ts); - if (rc != GM_OK) return GM_NOT_FOUND; + if (rc != GM_OK) return GM_ERR_NOT_FOUND; } rc = git_commit_lookup(&commit, repo, &oid); @@ -122,10 +128,10 @@ int gm_cache_load_meta(gm_context_t *ctx, const char *branch, gm_cache_meta_t *m } /* Synthesize metadata from repo state (no binary commit message parsing) */ - memset(meta, 0, sizeof *meta); + gm_memset_safe(meta, sizeof *meta, 0, sizeof *meta); meta->version = GM_CACHE_VERSION; meta->shard_bits = GM_CACHE_SHARD_BITS; - strncpy(meta->branch, branch, GM_CACHE_BRANCH_NAME_SIZE - 1); + (void)gm_strcpy_safe(meta->branch, GM_CACHE_BRANCH_NAME_SIZE, branch); meta->branch[GM_CACHE_BRANCH_NAME_SIZE - 1] = '\0'; meta->journal_tip_time = (uint64_t)git_commit_time(commit); git_commit_free(commit); @@ -134,23 +140,26 @@ int gm_cache_load_meta(gm_context_t *ctx, const char *branch, gm_cache_meta_t *m git_reference *journal_ref = NULL; char journal_ref_name[REF_NAME_BUFFER_SIZE]; { - int rn = gm_snprintf(journal_ref_name, sizeof(journal_ref_name), - "refs/gitmind/edges/%s", branch); - if (rn < 0 || (size_t)rn >= sizeof(journal_ref_name)) { - strcpy(meta->journal_tip_oid, ZERO_SHA_STRING); - /* continue with ZERO oid */ + int brc = gm_build_ref(journal_ref_name, sizeof(journal_ref_name), + GITMIND_EDGES_REF_PREFIX, branch); + if (brc != GM_OK) { + gm_memset_safe(&meta->journal_tip_oid_bin, sizeof(meta->journal_tip_oid_bin), 0, sizeof(meta->journal_tip_oid_bin)); + meta->journal_tip_oid[0] = '\0'; } } if (git_reference_lookup(&journal_ref, repo, journal_ref_name) == 0) { const git_oid *tip_oid = git_reference_target(journal_ref); if (tip_oid) { + meta->journal_tip_oid_bin = *tip_oid; git_oid_tostr(meta->journal_tip_oid, sizeof(meta->journal_tip_oid), tip_oid); } else { - strcpy(meta->journal_tip_oid, ZERO_SHA_STRING); + gm_memset_safe(&meta->journal_tip_oid_bin, sizeof(meta->journal_tip_oid_bin), 0, sizeof(meta->journal_tip_oid_bin)); + meta->journal_tip_oid[0] = '\0'; } git_reference_free(journal_ref); } else { - strcpy(meta->journal_tip_oid, ZERO_SHA_STRING); + gm_memset_safe(&meta->journal_tip_oid_bin, sizeof(meta->journal_tip_oid_bin), 0, sizeof(meta->journal_tip_oid_bin)); + meta->journal_tip_oid[0] = '\0'; } /* edge_count and build_time_ms unavailable without dedicated storage */ @@ -179,9 +188,9 @@ bool gm_cache_is_stale(gm_context_t *ctx, const char *branch) { git_reference *journal_ref = NULL; char journal_ref_name[REF_NAME_BUFFER_SIZE]; { - int rn = gm_snprintf(journal_ref_name, sizeof(journal_ref_name), - "refs/gitmind/edges/%s", branch); - if (rn < 0 || (size_t)rn >= sizeof(journal_ref_name)) { + int brc = gm_build_ref(journal_ref_name, sizeof(journal_ref_name), + GITMIND_EDGES_REF_PREFIX, branch); + if (brc != GM_OK) { return true; /* treat as stale if we can't build ref safely */ } } @@ -189,14 +198,24 @@ bool gm_cache_is_stale(gm_context_t *ctx, const char *branch) { if (git_reference_lookup(&journal_ref, repo, journal_ref_name) == 0) { const git_oid *current_tip = git_reference_target(journal_ref); if (current_tip) { - /* Compare with cached tip OID */ - git_oid cached_tip; - if (git_oid_fromstr(&cached_tip, meta.journal_tip_oid) == 0) { - /* If tips don't match, cache is stale */ - if (git_oid_cmp(current_tip, &cached_tip) != 0) { + /* Compare with cached tip OID (binary first) */ + if (!git_oid_is_zero(&meta.journal_tip_oid_bin)) { + if (git_oid_cmp(current_tip, &meta.journal_tip_oid_bin) != 0) { git_reference_free(journal_ref); return true; } + } else if (meta.journal_tip_oid[0] != '\0') { + git_oid cached_tip; + if (git_oid_fromstr(&cached_tip, meta.journal_tip_oid) == 0) { + if (git_oid_cmp(current_tip, &cached_tip) != 0) { + git_reference_free(journal_ref); + return true; + } + } + } else { + /* No previous tip known */ + git_reference_free(journal_ref); + return true; } } git_reference_free(journal_ref); @@ -219,13 +238,12 @@ static int load_bitmap_from_cache(git_repository *repo, git_tree *tree, /* Get shard prefix */ get_sha_prefix(sha, prefix, GM_CACHE_SHARD_BITS); - /* Convert SHA to hex */ - for (int i = 0; i < GM_OID_RAWSZ; i++) { - size_t off = (size_t)i * (size_t)HEX_CHARS_PER_BYTE; - (void)snprintf(sha_hex + off, (size_t)HEX_CHARS_PER_BYTE + 1, "%02x", - sha[i]); + /* Convert SHA to hex using libgit2 */ + { + git_oid tmp; + git_oid_fromraw(&tmp, sha); + git_oid_tostr(sha_hex, sizeof sha_hex, &tmp); } - sha_hex[(size_t)GM_OID_RAWSZ * (size_t)HEX_CHARS_PER_BYTE] = '\0'; /* Build path: prefix/sha.suffix */ { @@ -238,7 +256,7 @@ static int load_bitmap_from_cache(git_repository *repo, git_tree *tree, /* Look up tree entry */ rc = git_tree_entry_bypath(&entry, tree, path); if (rc < 0) - return GM_NOT_FOUND; + return GM_ERR_NOT_FOUND; /* Get blob */ rc = git_blob_lookup(&blob, repo, git_tree_entry_id(entry)); @@ -269,8 +287,8 @@ static int journal_scan_callback_generic(const gm_edge_t *edge, void *userdata) { journal_scan_state_t *state = (journal_scan_state_t *)userdata; const uint8_t *edge_sha = state->check_source - ? git_oid_raw(&edge->src_oid) - : git_oid_raw(&edge->tgt_oid); + ? (const uint8_t *)edge->src_oid.id + : (const uint8_t *)edge->tgt_oid.id; /* Check if this edge matches our query */ if (memcmp(edge_sha, state->target_sha, GM_OID_RAWSZ) == 0) { @@ -284,14 +302,14 @@ static int journal_scan_callback_generic(const gm_edge_t *edge, gm_edge_t *new_edges = realloc(state->edges, new_capacity * sizeof(gm_edge_t)); if (!new_edges) - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; state->edges = new_edges; state->capacity = new_capacity; } /* Copy edge */ - memcpy(&state->edges[state->count], edge, sizeof(gm_edge_t)); + (void)gm_memcpy_span(&state->edges[state->count], sizeof(gm_edge_t), edge, sizeof(gm_edge_t)); state->count++; } @@ -320,14 +338,14 @@ static int try_cache_query(git_repository *repo, const char *branch, /* Get cache commit */ { - int rn = gm_snprintf(ref_name, sizeof(ref_name), "%s%s", GM_CACHE_REF_PREFIX, branch); - if (rn < 0 || (size_t)rn >= sizeof(ref_name)) { - return GM_ERR_BUFFER_TOO_SMALL; + int brc = gm_build_ref(ref_name, sizeof(ref_name), GM_CACHE_REF_PREFIX, branch); + if (brc != GM_OK) { + return brc; } } rc = git_reference_name_to_id(&cache_oid, repo, ref_name); if (rc < 0) { - return GM_NOT_FOUND; + return GM_ERR_NOT_FOUND; } rc = git_commit_lookup(&cache_commit, repo, &cache_oid); @@ -356,7 +374,7 @@ static int try_cache_query(git_repository *repo, const char *branch, gm_bitmap_free(bitmap); if (!result->edge_ids && result->count > 0) { - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; } result->from_cache = true; @@ -375,7 +393,7 @@ static int fallback_journal_scan(gm_context_t *ctx, const char *branch, .check_source = check_source}; if (!state.edges) { - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; } int rc = @@ -389,7 +407,7 @@ static int fallback_journal_scan(gm_context_t *ctx, const char *branch, result->edge_ids = malloc(state.count * sizeof(uint32_t)); if (!result->edge_ids) { free(state.edges); - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; } for (size_t i = 0; i < state.count; i++) { @@ -411,7 +429,7 @@ static int cache_query_generic(git_repository *repo, const char *branch, int rc; /* Initialize result */ - memset(result, 0, sizeof(gm_cache_result_t)); + gm_memset_safe(result, sizeof(gm_cache_result_t), 0, sizeof(gm_cache_result_t)); ctx.git_repo = repo; /* Try cache first */ @@ -428,14 +446,14 @@ static int cache_query_generic(git_repository *repo, const char *branch, int gm_cache_query_fanout(gm_context_t *ctx, const char *branch, const gm_oid_t *src_oid, gm_cache_result_t *result) { git_repository *repo = (git_repository *)ctx->git_repo; - return cache_query_generic(repo, branch, git_oid_raw(src_oid), "forward", 1, result); + return cache_query_generic(repo, branch, (const uint8_t *)src_oid->id, "forward", 1, result); } /* Query edges by target SHA (reverse index) */ int gm_cache_query_fanin(gm_context_t *ctx, const char *branch, const gm_oid_t *tgt_oid, gm_cache_result_t *result) { git_repository *repo = (git_repository *)ctx->git_repo; - return cache_query_generic(repo, branch, git_oid_raw(tgt_oid), "reverse", 0, result); + return cache_query_generic(repo, branch, (const uint8_t *)tgt_oid->id, "reverse", 0, result); } /* Free cache result */ diff --git a/core/src/cache/tree_builder.c b/core/src/cache/tree_builder.c index 43bc5764..3522b666 100644 --- a/core/src/cache/tree_builder.c +++ b/core/src/cache/tree_builder.c @@ -18,10 +18,13 @@ #include #include "gitmind/cache.h" +#include "cache_internal.h" +#include "gitmind/error.h" #include "gitmind/constants.h" #include "gitmind/context.h" #include "gitmind/error.h" #include "gitmind/types.h" +#include "gitmind/security/string.h" #include #include @@ -55,8 +58,8 @@ static int add_file_to_tree(git_repository *repo, git_treebuilder *builder, static int process_fs_entry(git_repository *repo, git_treebuilder *dir_builder, const char *dir_path, const char *entry_name, const char *rel_path) { - char full_path[GM_PATH_MAX * 2]; - char entry_rel_path[GM_PATH_MAX * 2]; + char full_path[GM_PATH_MAX]; + char entry_rel_path[GM_PATH_MAX]; struct stat st; int rc; @@ -95,12 +98,12 @@ static int process_fs_entry(git_repository *repo, git_treebuilder *dir_builder, } rc = add_directory_to_tree(repo, dir_builder, full_path, entry_rel_path); - return (rc == GM_NOT_FOUND) ? GM_OK : rc; + return (rc == GM_ERR_NOT_FOUND) ? GM_OK : rc; } if (S_ISREG(st.st_mode)) { rc = add_file_to_tree(repo, dir_builder, full_path, entry_name); - return (rc == GM_NOT_FOUND) ? GM_OK : rc; + return (rc == GM_ERR_NOT_FOUND) ? GM_OK : rc; } /* Skip other file types (symlinks, devices, etc.) */ @@ -145,7 +148,7 @@ static int add_directory_to_tree(git_repository *repo, dir = opendir(dir_path); if (!dir) { - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } /* Create tree builder for this directory */ @@ -177,7 +180,7 @@ static int process_directory_entry(git_repository *repo, git_treebuilder *parent_builder, const char *dir_path, const char *entry_name) { - char full_path[GM_PATH_MAX * 2]; + char full_path[GM_PATH_MAX]; struct stat st; int rc; @@ -219,7 +222,7 @@ static int process_directory_entry(git_repository *repo, rc = git_treebuilder_insert(NULL, parent_builder, entry_name, &subtree_oid, GIT_FILEMODE_TREE); } - } else if (rc == GM_NOT_FOUND) { + } else if (rc == GM_ERR_NOT_FOUND) { rc = GM_OK; /* Directory disappeared - not fatal */ } @@ -232,7 +235,7 @@ static int build_directory_tree(git_repository *repo, git_treebuilder *builder, const char *dir_path) { DIR *dir = opendir(dir_path); if (!dir) { - return GM_IO_ERROR; + return GM_ERR_IO_FAILED; } struct dirent *entry; diff --git a/core/src/cache/tree_size.c b/core/src/cache/tree_size.c index 9db422a8..07483a2c 100644 --- a/core/src/cache/tree_size.c +++ b/core/src/cache/tree_size.c @@ -11,6 +11,7 @@ #include #include "gitmind/cache.h" +#include "cache_internal.h" #include "gitmind/error.h" /* Calculate size of a Git tree recursively */ diff --git a/core/src/edge/attributed.c b/core/src/edge/attributed.c index 14ea8ab2..180812a9 100644 --- a/core/src/edge/attributed.c +++ b/core/src/edge/attributed.c @@ -170,8 +170,8 @@ gm_result_void_t gm_edge_attributed_encode_cbor(const gm_edge_attributed_t *e, if (!r.ok) return gm_err_void(r.u.err); offset += r.u.val; /* OIDs */ - const uint8_t *src_raw = git_oid_raw(&e->src_oid); - const uint8_t *tgt_raw = git_oid_raw(&e->tgt_oid); + const uint8_t *src_raw = (const uint8_t *)e->src_oid.id; + const uint8_t *tgt_raw = (const uint8_t *)e->tgt_oid.id; r = gm_cbor_write_uint(GM_CBOR_KEY_SRC_OID, buffer + offset, avail - offset); if (!r.ok) return gm_err_void(r.u.err); offset += r.u.val; r = gm_cbor_write_bytes(buffer + offset, avail - offset, src_raw ? src_raw : e->src_sha, GM_OID_RAWSZ); @@ -280,8 +280,8 @@ static int decode_attr_ex_impl(const uint8_t *buffer, size_t len, } } - if (git_oid_iszero(&out.src_oid)) git_oid_fromraw(&out.src_oid, out.src_sha); - if (git_oid_iszero(&out.tgt_oid)) git_oid_fromraw(&out.tgt_oid, out.tgt_sha); + if (git_oid_is_zero(&out.src_oid)) git_oid_fromraw(&out.src_oid, out.src_sha); + if (git_oid_is_zero(&out.tgt_oid)) git_oid_fromraw(&out.tgt_oid, out.tgt_sha); *e = out; *consumed = offset; return GM_OK; } diff --git a/core/src/edge/edge.c b/core/src/edge/edge.c index 06bab682..f0ac74b1 100644 --- a/core/src/edge/edge.c +++ b/core/src/edge/edge.c @@ -10,6 +10,8 @@ #include "gitmind/result.h" #include "gitmind/types/ulid.h" #include "gitmind/security/string.h" +#include "gitmind/security/memory.h" +#include "gitmind/util/memory.h" #include "gitmind/cbor/keys.h" #include #include @@ -43,8 +45,7 @@ static uint64_t get_timestamp_millis(gm_context_t *ctx) { * Initialize edge with default values */ static void edge_init_defaults(gm_edge_t *edge) { - // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) - (void)memset(edge, 0, sizeof(gm_edge_t)); + (void)gm_memset_safe(edge, sizeof(gm_edge_t), 0, sizeof(gm_edge_t)); edge->confidence = DefaultConfidence; } @@ -107,13 +108,9 @@ gm_result_edge_t gm_edge_create(gm_context_t *ctx, const char *src_path, git_oid_fromraw(&edge.tgt_oid, edge.tgt_sha); /* Set fields */ - // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) - (void)strncpy(edge.src_path, src_path, GM_PATH_MAX - 1); - edge.src_path[GM_PATH_MAX - 1] = '\0'; + (void)gm_strcpy_safe(edge.src_path, GM_PATH_MAX, src_path); - // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) - (void)strncpy(edge.tgt_path, tgt_path, GM_PATH_MAX - 1); - edge.tgt_path[GM_PATH_MAX - 1] = '\0'; + (void)gm_strcpy_safe(edge.tgt_path, GM_PATH_MAX, tgt_path); edge.rel_type = (uint16_t)rel_type; edge.timestamp = get_timestamp_millis(ctx); @@ -135,22 +132,26 @@ bool gm_edge_equal(const gm_edge_t *edge_a, const gm_edge_t *edge_b) { return false; } - /* Source: require OID equality when both OIDs are set; otherwise fallback to legacy SHA */ - if (!git_oid_iszero(&edge_a->src_oid) && !git_oid_iszero(&edge_b->src_oid)) { + /* Source: if both OIDs present, they must match; otherwise fallback to legacy SHA */ + if (!git_oid_is_zero(&edge_a->src_oid) && !git_oid_is_zero(&edge_b->src_oid)) { if (git_oid_cmp(&edge_a->src_oid, &edge_b->src_oid) != 0) { return false; } - } else if (memcmp(edge_a->src_sha, edge_b->src_sha, GM_SHA1_SIZE) != 0) { - return false; + } else { + if (memcmp(edge_a->src_sha, edge_b->src_sha, GM_SHA1_SIZE) != 0) { + return false; + } } /* Target: same rule as Source */ - if (!git_oid_iszero(&edge_a->tgt_oid) && !git_oid_iszero(&edge_b->tgt_oid)) { + if (!git_oid_is_zero(&edge_a->tgt_oid) && !git_oid_is_zero(&edge_b->tgt_oid)) { if (git_oid_cmp(&edge_a->tgt_oid, &edge_b->tgt_oid) != 0) { return false; } - } else if (memcmp(edge_a->tgt_sha, edge_b->tgt_sha, GM_SHA1_SIZE) != 0) { - return false; + } else { + if (memcmp(edge_a->tgt_sha, edge_b->tgt_sha, GM_SHA1_SIZE) != 0) { + return false; + } } /* Relationship type must match */ @@ -333,8 +334,8 @@ gm_result_void_t gm_edge_encode_cbor(const gm_edge_t *edge, uint8_t *buffer, return result; } /* Write preferred OID fields using raw bytes */ - const uint8_t *src_raw = git_oid_raw(&edge->src_oid); - const uint8_t *tgt_raw = git_oid_raw(&edge->tgt_oid); + const uint8_t *src_raw = (const uint8_t *)edge->src_oid.id; + const uint8_t *tgt_raw = (const uint8_t *)edge->tgt_oid.id; if (src_raw == NULL) src_raw = edge->src_sha; /* fallback */ if (tgt_raw == NULL) tgt_raw = edge->tgt_sha; /* fallback */ result = write_cbor_bytes(GM_CBOR_KEY_SRC_OID, buffer, available, &offset, src_raw, GM_OID_RAWSZ); @@ -423,6 +424,20 @@ static gm_result_void_t decode_cbor_field(const uint8_t *buffer, size_t *offset, case GM_CBOR_KEY_ULID: { return decode_cbor_text(buffer, offset, len, edge->ulid, GM_ULID_SIZE + 1); } + case GM_CBOR_KEY_SRC_OID: { + uint8_t raw[GM_OID_RAWSZ]; + gm_result_void_t r = gm_cbor_read_bytes(buffer, offset, len, raw, GM_OID_RAWSZ); + if (!r.ok) return r; + git_oid_fromraw(&edge->src_oid, raw); + return gm_ok_void(); + } + case GM_CBOR_KEY_TGT_OID: { + uint8_t raw[GM_OID_RAWSZ]; + gm_result_void_t r = gm_cbor_read_bytes(buffer, offset, len, raw, GM_OID_RAWSZ); + if (!r.ok) return r; + git_oid_fromraw(&edge->tgt_oid, raw); + return gm_ok_void(); + } default: return gm_err_void(GM_ERROR(GM_ERR_INVALID_FORMAT, "Unknown CBOR key")); } @@ -459,10 +474,10 @@ static int gm_edge_decode_cbor_ex_impl(const uint8_t *buffer, size_t len, gm_edg } /* Backfill OIDs from legacy SHA if missing */ - if (git_oid_iszero(&edge.src_oid)) { + if (git_oid_is_zero(&edge.src_oid)) { git_oid_fromraw(&edge.src_oid, edge.src_sha); } - if (git_oid_iszero(&edge.tgt_oid)) { + if (git_oid_is_zero(&edge.tgt_oid)) { git_oid_fromraw(&edge.tgt_oid, edge.tgt_sha); } diff --git a/core/src/hooks/augment.c b/core/src/hooks/augment.c index 5c95a99d..d3715d03 100644 --- a/core/src/hooks/augment.c +++ b/core/src/hooks/augment.c @@ -4,6 +4,7 @@ #include "gitmind/hooks/augment.h" #include +#include "gitmind/util/memory.h" #include /* Note: replace any private header usage with public equivalents when available. */ @@ -33,21 +34,21 @@ int gm_hook_get_blob_sha(git_repository *repo, const char *commit_ref, /* Resolve commit reference */ error = git_revparse_single(&obj, repo, commit_ref); if (error < 0) { - return GM_NOT_FOUND; + return GM_ERR_NOT_FOUND; } /* Get commit from object */ error = git_commit_lookup(&commit, repo, git_object_id(obj)); git_object_free(obj); if (error < 0) { - return GM_NOT_FOUND; + return GM_ERR_NOT_FOUND; } /* Get tree from commit */ error = git_commit_tree(&tree, commit); git_commit_free(commit); if (error < 0) { - return GM_NOT_FOUND; + return GM_ERR_NOT_FOUND; } /* Look up file in tree */ @@ -60,7 +61,7 @@ int gm_hook_get_blob_sha(git_repository *repo, const char *commit_ref, /* Ensure it's a blob (not directory/submodule) */ if (git_tree_entry_type(entry) != GIT_OBJECT_BLOB) { git_tree_entry_free(entry); - return GM_NOT_FOUND; + return GM_ERR_NOT_FOUND; } /* Copy OID */ @@ -98,7 +99,7 @@ static int edge_search_callback(const gm_edge_t *edge, void *userdata) { gm_edge_t *new_edges = realloc(ctx->edges, new_capacity * sizeof(gm_edge_t)); if (!new_edges) { - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; } ctx->edges = new_edges; ctx->capacity = new_capacity; @@ -122,7 +123,7 @@ int gm_hook_find_edges_by_source(gm_context_t *ctx, const gm_oid_t *src_oid, .scanned = 0}; if (!search_ctx.edges) { - return GM_NO_MEMORY; + return GM_ERR_OUT_OF_MEMORY; } /* Walk journal looking for edges */ @@ -150,11 +151,11 @@ int gm_hook_create_augments_edge(gm_context_t *ctx, const gm_oid_t *old_oid, edge.confidence = AUGMENT_CONFIDENCE; /* Always 100% for augments */ edge.timestamp = (uint64_t)time(NULL); - /* Set paths (both same for AUGMENTS) */ - strncpy(edge.src_path, file_path, GM_PATH_MAX - 1); - edge.src_path[GM_PATH_MAX - 1] = '\0'; - strncpy(edge.tgt_path, file_path, GM_PATH_MAX - 1); - edge.tgt_path[GM_PATH_MAX - 1] = '\0'; + /* Set paths (both same for AUGMENTS); fail on truncation */ + if (gm_strcpy_safe(edge.src_path, GM_PATH_MAX, file_path) == -1 || + gm_strcpy_safe(edge.tgt_path, GM_PATH_MAX, file_path) == -1) { + return GM_ERR_BUFFER_TOO_SMALL; + } /* Generate ULID */ gm_ulid_generate(edge.ulid); diff --git a/core/src/hooks/post-commit.c b/core/src/hooks/post-commit.c index cf4180ec..9f50affc 100644 --- a/core/src/hooks/post-commit.c +++ b/core/src/hooks/post-commit.c @@ -9,6 +9,7 @@ #include "../../include/gitmind/constants_internal.h" #include "augment.h" +#include /* Array management constants */ #define INITIAL_FILE_ARRAY_SIZE 10 @@ -228,7 +229,7 @@ int main(int argc, char **argv) { /* Process files if not too many */ if (file_count <= MAX_CHANGED_FILES) { /* Initialize context */ - memset(&ctx, 0, sizeof(ctx)); + gm_memset_safe(&ctx, sizeof(ctx), 0, sizeof(ctx)); ctx.git_ops.resolve_blob = resolve_blob; ctx.git_ops.create_commit = create_commit; ctx.git_ops.read_commits = read_commits; diff --git a/core/src/journal/reader.c b/core/src/journal/reader.c index 3a267b6c..05ce0bae 100644 --- a/core/src/journal/reader.c +++ b/core/src/journal/reader.c @@ -6,6 +6,8 @@ #include "gitmind/context.h" #include "gitmind/cbor/constants_cbor.h" #include "gitmind/cbor/cbor.h" +#include "gitmind/constants_internal.h" +#include "gitmind/util/ref.h" #include "gitmind/cbor/keys.h" #include "gitmind/error.h" #include "gitmind/edge.h" @@ -25,14 +27,23 @@ #include #include #include "gitmind/security/memory.h" -#include "gitmind/security/string.h" +#include +#include "gitmind/util/memory.h" /* Constants */ -#define REFS_GITMIND_PREFIX "refs/gitmind/edges/" #define MAX_CBOR_SIZE CBOR_MAX_STRING_LENGTH -#define REF_NAME_BUFFER_SIZE GM_PATH_MAX #define CURRENT_BRANCH_BUFFER_SIZE GM_PATH_MAX +/* Debug flag for CBOR decoding (set GITMIND_CBOR_DEBUG=1) */ +static int g_cbor_debug = -1; +static inline int cbor_debug_enabled(void) { + if (g_cbor_debug == -1) { + const char *v = getenv("GITMIND_CBOR_DEBUG"); + g_cbor_debug = (v && (v[0] == '1' || v[0] == 't' || v[0] == 'T' || v[0] == 'y' || v[0] == 'Y')) ? 1 : 0; + } + return g_cbor_debug; +} + /* Forward declarations */ int gm_edge_decode_cbor_ex(const uint8_t *buffer, size_t len, gm_edge_t *edge, size_t *consumed); @@ -95,7 +106,7 @@ static void convert_legacy_to_attributed(const gm_edge_t *legacy, attributed->attribution.author[0] = '\0'; attributed->attribution.session_id[0] = '\0'; attributed->attribution.flags = 0; - attributed->lane = GM_LANE_PRIMARY; + attributed->lane = GM_LANE_DEFAULT; } /* (Removed local attributed CBOR decoder; use public edge API) */ @@ -104,7 +115,7 @@ static void convert_legacy_to_attributed(const gm_edge_t *legacy, static int process_attributed_edge(const uint8_t *cbor_data, size_t remaining, reader_ctx_t *rctx, size_t *consumed) { gm_edge_attributed_t edge; - memset(&edge, 0, sizeof(edge)); + gm_memset_safe(&edge, sizeof(edge), 0, sizeof(edge)); /* Try to decode an attributed edge */ int decode_result = gm_edge_attributed_decode_cbor_ex(cbor_data, remaining, @@ -117,6 +128,9 @@ static int process_attributed_edge(const uint8_t *cbor_data, size_t remaining, decode_result = gm_edge_decode_cbor_ex(cbor_data, remaining, &legacy_edge, &legacy_consumed); if (decode_result != GM_OK || legacy_consumed == 0) { + if (cbor_debug_enabled()) { + fprintf(stderr, "[CBOR DEBUG] Attributed decode failed at offset=%zu remaining=%zu\n", (size_t)0, remaining); + } return GM_ERR_INVALID_FORMAT; } @@ -146,23 +160,23 @@ static int process_regular_edge(const uint8_t *cbor_data, size_t remaining, size_t aconsumed = 0; decode_result = gm_edge_attributed_decode_cbor_ex(cbor_data, remaining, &aedge, &aconsumed); if (decode_result != GM_OK || aconsumed == 0) { + if (cbor_debug_enabled()) { + fprintf(stderr, "[CBOR DEBUG] Regular decode failed; attributed decode also failed at offset=%zu remaining=%zu\n", (size_t)0, remaining); + } return GM_ERR_INVALID_FORMAT; } gm_edge_t basic = {0}; - memcpy(basic.src_sha, aedge.src_sha, GM_SHA1_SIZE); - memcpy(basic.tgt_sha, aedge.tgt_sha, GM_SHA1_SIZE); + (void)gm_memcpy_span(basic.src_sha, GM_SHA1_SIZE, aedge.src_sha, GM_SHA1_SIZE); + (void)gm_memcpy_span(basic.tgt_sha, GM_SHA1_SIZE, aedge.tgt_sha, GM_SHA1_SIZE); basic.src_oid = aedge.src_oid; basic.tgt_oid = aedge.tgt_oid; basic.rel_type = aedge.rel_type; basic.confidence = aedge.confidence; basic.timestamp = aedge.timestamp; - strncpy(basic.src_path, aedge.src_path, GM_PATH_MAX - 1); - basic.src_path[GM_PATH_MAX - 1] = '\0'; - strncpy(basic.tgt_path, aedge.tgt_path, GM_PATH_MAX - 1); - basic.tgt_path[GM_PATH_MAX - 1] = '\0'; - strncpy(basic.ulid, aedge.ulid, GM_ULID_SIZE); - basic.ulid[GM_ULID_SIZE] = '\0'; + (void)gm_strcpy_safe(basic.src_path, GM_PATH_MAX, aedge.src_path); + (void)gm_strcpy_safe(basic.tgt_path, GM_PATH_MAX, aedge.tgt_path); + (void)gm_strcpy_safe(basic.ulid, GM_ULID_SIZE + 1, aedge.ulid); *consumed = aconsumed; return ((int (*)(const gm_edge_t *, void *))rctx->callback)(&basic, rctx->userdata); @@ -209,6 +223,9 @@ static int process_commit_generic(git_commit *commit, reader_ctx_t *rctx) { } if (cb_result == GM_ERR_INVALID_FORMAT) { + if (cbor_debug_enabled()) { + fprintf(stderr, "[CBOR DEBUG] Invalid CBOR at commit decode offset=%zu remaining=%zu\n", offset, remaining); + } break; /* No more edges to decode */ } @@ -216,6 +233,9 @@ static int process_commit_generic(git_commit *commit, reader_ctx_t *rctx) { return cb_result; /* Callback requested stop */ } + if (cbor_debug_enabled()) { + fprintf(stderr, "[CBOR DEBUG] Decoded an edge (consumed=%zu) at offset=%zu\n", consumed, offset); + } offset += consumed; } @@ -324,11 +344,8 @@ static int journal_read_generic(gm_context_t *ctx, const char *branch, } /* Build ref name */ - { - int rn = gm_snprintf(ref_name, sizeof(ref_name), "%s%s", REFS_GITMIND_PREFIX, branch); - if (rn < 0 || (size_t)rn >= sizeof(ref_name)) { - return GM_ERR_BUFFER_TOO_SMALL; - } + if (gm_build_ref(ref_name, sizeof(ref_name), GITMIND_EDGES_REF_PREFIX, branch) != GM_OK) { + return GM_ERR_BUFFER_TOO_SMALL; } /* Walk the journal */ diff --git a/core/src/journal/writer.c b/core/src/journal/writer.c index d3f08830..30e56d30 100644 --- a/core/src/journal/writer.c +++ b/core/src/journal/writer.c @@ -9,7 +9,7 @@ #include "gitmind/result.h" #include "gitmind/edge.h" #include "gitmind/edge_attributed.h" -#include "gitmind/security/string.h" +#include "gitmind/security/memory.h" #include #include @@ -24,14 +24,12 @@ #include /* Local constants */ -#define REFS_GITMIND_PREFIX "refs/gitmind/edges/" +#include "gitmind/constants_internal.h" +#include "gitmind/util/ref.h" #define MAX_CBOR_SIZE CBOR_MAX_STRING_LENGTH -#define REF_NAME_BUFFER_SIZE GM_PATH_MAX #define COMMIT_ENCODING "UTF-8" #define CBOR_OVERFLOW_MARGIN GM_FORMAT_BUFFER_SIZE /* CBOR encoding safety margin */ #define PARENT_COMMITS_MAX 1 -#define EMPTY_TREE_SHA "4b825dc642cb6eb9a060e54bf8d69288fbee4904" -#define BRANCH_BUFFER_SIZE GM_PATH_MAX /* Journal writer context */ typedef struct { @@ -51,9 +49,9 @@ static int journal_init(journal_ctx_t *jctx, gm_context_t *ctx, } /* Build ref name */ - int nref = gm_snprintf(jctx->ref_name, sizeof(jctx->ref_name), "%s%s", - REFS_GITMIND_PREFIX, branch); - if (nref < 0 || (size_t)nref >= sizeof(jctx->ref_name)) { + int nref = gm_build_ref(jctx->ref_name, sizeof(jctx->ref_name), + GITMIND_EDGES_REF_PREFIX, branch); + if (nref != GM_OK) { return GM_ERR_BUFFER_TOO_SMALL; } @@ -174,7 +172,7 @@ static int journal_append_generic(gm_context_t *ctx, const void *edges, size_t n_edges, size_t edge_size, edge_encoder_fn encoder) { journal_ctx_t jctx; - char branch[BRANCH_BUFFER_SIZE]; + char branch[GM_PATH_MAX]; uint8_t *cbor_buffer = NULL; size_t offset = 0; git_oid commit_oid; @@ -184,9 +182,17 @@ static int journal_append_generic(gm_context_t *ctx, const void *edges, return GM_ERR_INVALID_ARGUMENT; } - /* Get current branch */ - if (get_current_branch(ctx->git_repo, branch, sizeof(branch)) != GM_OK) { - return GM_ERR_UNKNOWN; + /* Determine branch: prefer explicit env for tests, else current branch */ + const char *env_branch = getenv("GITMIND_TEST_BRANCH"); + if (env_branch && *env_branch) { + size_t n = strlen(env_branch); + if (n >= sizeof(branch)) n = sizeof(branch) - 1; + gm_memcpy_safe(branch, sizeof(branch), env_branch, n); + branch[n] = '\0'; + } else { + if (get_current_branch(ctx->git_repo, branch, sizeof(branch)) != GM_OK) { + return GM_ERR_UNKNOWN; + } } /* Initialize journal context */ diff --git a/core/src/util/ref.c b/core/src/util/ref.c new file mode 100644 index 00000000..6bcaeff1 --- /dev/null +++ b/core/src/util/ref.c @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */ +/* © 2025 J. Kirby Ross / Neuroglyph Collective */ + +#include "gitmind/util/ref.h" +#include "gitmind/error.h" +#include "gitmind/security/string.h" +#include "gitmind/constants_internal.h" +#include + +#include + +int gm_build_ref(char *out, size_t out_sz, const char *prefix, + const char *branch) { + if (!out || out_sz == 0 || !prefix || !branch) { + if (out && out_sz > 0) out[0] = '\0'; + return GM_ERR_INVALID_ARGUMENT; + } + out[0] = '\0'; + if (*branch == '\0') { + return GM_ERR_INVALID_ARGUMENT; + } + /* Require shorthand branch (no leading "refs/") to avoid double prefixing */ + if (branch[0] == 'r' && branch[1] == 'e' && branch[2] == 'f' && + branch[3] == 's' && branch[4] == '/') { + return GM_ERR_INVALID_ARGUMENT; + } + + char candidate[REF_NAME_BUFFER_SIZE]; + int rn = gm_snprintf(candidate, sizeof candidate, "%s%s", prefix, branch); + if (rn < 0) { + return GM_ERR_UNKNOWN; + } + if ((size_t)rn >= sizeof candidate) { + return GM_ERR_BUFFER_TOO_SMALL; + } + + /* Minimal validity checks without libgit2 dependency */ + { + const char *p = branch; + if (*p == '/' || p[strlen(p) - 1] == '/') { + return GM_ERR_INVALID_ARGUMENT; + } + for (; *p; ++p) { + const char c = *p; + if (c == '~' || c == '^' || c == ':' || c == '?' || + c == '[' || c == '*' || c == '\\') { + return GM_ERR_INVALID_ARGUMENT; + } + } + if (strstr(branch, "..") != NULL) return GM_ERR_INVALID_ARGUMENT; + if (strstr(branch, "@{") != NULL) return GM_ERR_INVALID_ARGUMENT; + } + + rn = gm_snprintf(out, out_sz, "%s", candidate); + if (rn < 0) { + out[0] = '\0'; + return GM_ERR_UNKNOWN; + } + if ((size_t)rn >= out_sz) { + out[0] = '\0'; + return GM_ERR_BUFFER_TOO_SMALL; + } + return GM_OK; +} diff --git a/core/tests/unit/test_cache_branch_limits.c b/core/tests/unit/test_cache_branch_limits.c new file mode 100644 index 00000000..4348e1c6 --- /dev/null +++ b/core/tests/unit/test_cache_branch_limits.c @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */ +/* © 2025 J. Kirby Ross / Neuroglyph Collective */ + +#include +#include +#include + +#include +#include + +#include "gitmind/cache.h" +#include "gitmind/context.h" +#include "gitmind/edge.h" +#include "gitmind/error.h" +#include "gitmind/journal.h" +#include "gitmind/types.h" +#include "gitmind/types/ulid.h" + +static void set_user_config(git_repository *repo) { + git_config *cfg = NULL; + int rc = git_repository_config(&cfg, repo); + assert(rc == 0); + rc = git_config_set_string(cfg, "user.name", "Tester"); + assert(rc == 0); + rc = git_config_set_string(cfg, "user.email", "tester@example.com"); + assert(rc == 0); + git_config_free(cfg); +} + +static void ensure_branch_with_commit(git_repository *repo, const char *branch) { + git_treebuilder *tb = NULL; + git_oid tree_oid, commit_oid; + git_signature *sig = NULL; + git_reference *ref = NULL; + char refname[256]; + int rc; + + rc = git_treebuilder_new(&tb, repo, NULL); + assert(rc == 0); + rc = git_treebuilder_write(&tree_oid, tb); + git_treebuilder_free(tb); + assert(rc == 0); + + rc = git_signature_now(&sig, "tester", "tester@example.com"); + assert(rc == 0); + + snprintf(refname, sizeof refname, "refs/heads/%s", branch); + rc = git_commit_create_from_ids(&commit_oid, repo, NULL, sig, sig, NULL, + "init", &tree_oid, 0, NULL); + git_signature_free(sig); + assert(rc == 0); + + rc = git_reference_create(&ref, repo, refname, &commit_oid, 1, "init"); + if (ref != NULL) { + git_reference_free(ref); + } + assert(rc == 0); + + rc = git_repository_set_head(repo, refname); + assert(rc == 0); +} + +static void append_dummy_edge(gm_context_t *ctx) { + gm_edge_t edge; + memset(&edge, 0, sizeof edge); + + uint8_t src_raw[GM_OID_RAWSZ]; + uint8_t tgt_raw[GM_OID_RAWSZ]; + memset(src_raw, 0x11, sizeof src_raw); + memset(tgt_raw, 0x22, sizeof tgt_raw); + + git_oid_fromraw(&edge.src_oid, src_raw); + git_oid_fromraw(&edge.tgt_oid, tgt_raw); + + edge.rel_type = GM_REL_IMPLEMENTS; + edge.confidence = 0x3C00; + strcpy(edge.src_path, "A"); + strcpy(edge.tgt_path, "B"); + (void)gm_ulid_generate(edge.ulid); + + int rc = gm_journal_append(ctx, &edge, 1); + assert(rc == GM_OK); +} + +int main(void) { + printf("test_cache_branch_limits... "); + git_libgit2_init(); + + git_repository *repo = NULL; + int rc = git_repository_init(&repo, "./.gm_cache_branch_tmp", true); + assert(rc == 0 && repo); + set_user_config(repo); + + char valid_branch[GM_CACHE_BRANCH_NAME_SIZE]; + memset(valid_branch, 'a', sizeof valid_branch); + valid_branch[GM_CACHE_BRANCH_NAME_SIZE - 1] = '\0'; + ensure_branch_with_commit(repo, valid_branch); + + gm_context_t ctx = {0}; + ctx.git_repo = repo; + + append_dummy_edge(&ctx); + rc = gm_cache_rebuild(&ctx, valid_branch, true); + assert(rc == GM_OK); + + char invalid_branch[GM_CACHE_BRANCH_NAME_SIZE + 1]; + memset(invalid_branch, 'b', sizeof invalid_branch); + invalid_branch[GM_CACHE_BRANCH_NAME_SIZE] = '\0'; + ensure_branch_with_commit(repo, invalid_branch); + append_dummy_edge(&ctx); + + rc = gm_cache_rebuild(&ctx, invalid_branch, true); + assert(rc == GM_ERR_INVALID_ARGUMENT); + + git_repository_free(repo); + git_libgit2_shutdown(); + printf("OK\n"); + return 0; +} diff --git a/core/tests/unit/test_cache_meta.c b/core/tests/unit/test_cache_meta.c index 645a2137..5139a9c9 100644 --- a/core/tests/unit/test_cache_meta.c +++ b/core/tests/unit/test_cache_meta.c @@ -5,36 +5,38 @@ #include #include #include +#include #include "gitmind/cache.h" #include "gitmind/context.h" +#include "gitmind/error.h" -static void make_temp_ref(git_repository *repo, const char *refname) { +static void make_temp_ref(git_repository *repo, const char *legacy_refname) { git_treebuilder *tb = NULL; git_oid tree_oid, commit_oid; git_signature *sig = NULL; + git_reference *legacy = NULL; int rc; - rc = git_treebuilder_new(&tb, NULL); + rc = git_treebuilder_new(&tb, repo, NULL); assert(rc == 0); - rc = git_treebuilder_write(&tree_oid, repo, tb); + rc = git_treebuilder_write(&tree_oid, tb); git_treebuilder_free(tb); assert(rc == 0); rc = git_signature_now(&sig, "tester", "tester@example.com"); assert(rc == 0); - rc = git_commit_create_v(&commit_oid, repo, refname, sig, sig, NULL, - "cache", (git_tree*)NULL, 0); - /* Older libgit2 requires tree; re-create with tree lookup */ - if (rc < 0) { - git_tree *tree = NULL; - rc = git_tree_lookup(&tree, repo, &tree_oid); - assert(rc == 0); - rc = git_commit_create(&commit_oid, repo, refname, sig, sig, NULL, - "cache", tree, 0, NULL); - git_tree_free(tree); - assert(rc == 0); - } + + /* Create a commit object directly (no ref update) */ + rc = git_commit_create_from_ids(&commit_oid, repo, NULL, sig, sig, NULL, + "cache", &tree_oid, 0, NULL); + assert(rc == 0); + + /* Create the legacy ref pointing to that commit */ + rc = git_reference_create(&legacy, repo, legacy_refname, &commit_oid, 1, + "legacy cache ref"); + assert(rc == 0); + git_reference_free(legacy); git_signature_free(sig); } @@ -62,4 +64,3 @@ int main(void) { printf("OK\n"); return 0; } - diff --git a/core/tests/unit/test_cache_query.c b/core/tests/unit/test_cache_query.c index b8efc875..7297bfde 100644 --- a/core/tests/unit/test_cache_query.c +++ b/core/tests/unit/test_cache_query.c @@ -7,10 +7,44 @@ #include #include "gitmind/cache.h" +#include +#include "gitmind/types/ulid.h" +#include "gitmind/error.h" #include "gitmind/journal.h" #include "gitmind/edge.h" #include "gitmind/types.h" +static void ensure_branch_with_commit(git_repository *repo, const char *branch) { + git_treebuilder *tb = NULL; + git_oid tree_oid, commit_oid; + git_signature *sig = NULL; + int rc; + + rc = git_treebuilder_new(&tb, repo, NULL); + assert(rc == 0); + rc = git_treebuilder_write(&tree_oid, tb); + git_treebuilder_free(tb); + assert(rc == 0); + + rc = git_signature_now(&sig, "tester", "tester@example.com"); + assert(rc == 0); + + char refname[128]; + snprintf(refname, sizeof refname, "refs/heads/%s", branch); + + /* Create a commit object and then a direct ref to it */ + rc = git_commit_create_from_ids(&commit_oid, repo, NULL, sig, sig, NULL, + "init", &tree_oid, 0, NULL); + git_signature_free(sig); + assert(rc == 0); + git_reference *ref = NULL; + rc = git_reference_create(&ref, repo, refname, &commit_oid, 1, "init"); + if (ref) git_reference_free(ref); + assert(rc == 0); + + rc = git_repository_set_head(repo, refname); + assert(rc == 0); +} static void set_user_config(git_repository *repo) { git_config *cfg = NULL; int rc = git_repository_config(&cfg, repo); @@ -30,6 +64,7 @@ int main(void) { int rc = git_repository_init(&repo, "./.gm_cache_query_tmp", true); assert(rc == 0 && repo); set_user_config(repo); + ensure_branch_with_commit(repo, "testq"); gm_context_t ctx = {0}; ctx.git_repo = repo; @@ -70,4 +105,3 @@ int main(void) { printf("OK\n"); return 0; } - diff --git a/core/tests/unit/test_cache_shard_distribution.c b/core/tests/unit/test_cache_shard_distribution.c new file mode 100644 index 00000000..79c9cbb1 --- /dev/null +++ b/core/tests/unit/test_cache_shard_distribution.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */ +/* © 2025 J. Kirby Ross / Neuroglyph Collective */ + +#include +#include +#include +#include + +#include "gitmind/types.h" + +/* Simple LCG for deterministic pseudo-random bytes */ +static uint32_t lcg_state = 0x12345678u; +static uint32_t lcg_next(void) { + lcg_state = 1664525u * lcg_state + 1013904223u; + return lcg_state; +} + +static void random_oid(gm_oid_t *oid) { + uint8_t raw[GM_OID_RAWSZ]; + for (int i = 0; i < GM_OID_RAWSZ; i++) raw[i] = (uint8_t)(lcg_next() & 0xFF); + git_oid_fromraw(oid, raw); +} + +int main(void) { + printf("test_cache_shard_distribution... "); + enum { N = 10000 }; + uint32_t buckets[256]; + memset(buckets, 0, sizeof buckets); + + for (int i = 0; i < N; i++) { + gm_oid_t oid = {0}; + random_oid(&oid); + const uint8_t *raw = (const uint8_t *)oid.id; + buckets[raw[0]]++; + } + + /* Check very weak uniformity: each bucket should be within [0, N/8] for this sample. */ + /* This is a sanity smoke-test, not a statistical proof. */ + uint32_t max_bucket = 0; + for (int i = 0; i < 256; i++) { + if (buckets[i] > max_bucket) max_bucket = buckets[i]; + } + assert(max_bucket < (uint32_t)(N / 8)); + printf("OK\n"); + return 0; +} diff --git a/core/tests/unit/test_cache_tree_size.c b/core/tests/unit/test_cache_tree_size.c new file mode 100644 index 00000000..66199dce --- /dev/null +++ b/core/tests/unit/test_cache_tree_size.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */ +/* © 2025 J. Kirby Ross / Neuroglyph Collective */ + +#include +#include +#include + +#include + +#include "gitmind/error.h" + +int gm_cache_calculate_size(git_repository *repo, const git_oid *tree_oid, + uint64_t *size_bytes); + +static size_t object_size(git_repository *repo, const git_oid *oid) { + git_odb *odb = NULL; + git_odb_object *obj = NULL; + int rc = git_repository_odb(&odb, repo); + assert(rc == 0); + rc = git_odb_read(&obj, odb, oid); + git_odb_free(odb); + assert(rc == 0 && obj != NULL); + size_t size = git_odb_object_size(obj); + git_odb_object_free(obj); + return size; +} + +int main(void) { + printf("test_cache_tree_size... "); + git_libgit2_init(); + + git_repository *repo = NULL; + int rc = git_repository_init(&repo, "./.gm_cache_tree_tmp", true); + assert(rc == 0 && repo); + + const char *root_content = "root"; + const char *nested_content = "nested-data"; + + git_oid root_blob_oid; + rc = git_blob_create_frombuffer(&root_blob_oid, repo, + root_content, strlen(root_content)); + assert(rc == 0); + + git_oid nested_blob_oid; + rc = git_blob_create_frombuffer(&nested_blob_oid, repo, + nested_content, strlen(nested_content)); + assert(rc == 0); + + /* Build nested tree */ + git_treebuilder *tb = NULL; + git_oid nested_tree_oid; + rc = git_treebuilder_new(&tb, repo, NULL); + assert(rc == 0); + rc = git_treebuilder_insert(NULL, tb, "nested.txt", &nested_blob_oid, + GIT_FILEMODE_BLOB); + assert(rc == 0); + rc = git_treebuilder_write(&nested_tree_oid, tb); + git_treebuilder_free(tb); + assert(rc == 0); + + /* Build root tree */ + rc = git_treebuilder_new(&tb, repo, NULL); + assert(rc == 0); + rc = git_treebuilder_insert(NULL, tb, "root.txt", &root_blob_oid, + GIT_FILEMODE_BLOB); + assert(rc == 0); + rc = git_treebuilder_insert(NULL, tb, "nested", &nested_tree_oid, + GIT_FILEMODE_TREE); + assert(rc == 0); + git_oid root_tree_oid; + rc = git_treebuilder_write(&root_tree_oid, tb); + git_treebuilder_free(tb); + assert(rc == 0); + + uint64_t total = 0; + rc = gm_cache_calculate_size(repo, &root_tree_oid, &total); + assert(rc == GM_OK); + + size_t expected = 0; + expected += object_size(repo, &root_tree_oid); + expected += object_size(repo, &nested_tree_oid); + expected += object_size(repo, &root_blob_oid); + expected += object_size(repo, &nested_blob_oid); + + assert(total == (uint64_t)expected); + + git_repository_free(repo); + git_libgit2_shutdown(); + printf("OK\n"); + return 0; +} diff --git a/core/tests/unit/test_edge_attributed_cbor.c b/core/tests/unit/test_edge_attributed_cbor.c index cebf1371..dc794922 100644 --- a/core/tests/unit/test_edge_attributed_cbor.c +++ b/core/tests/unit/test_edge_attributed_cbor.c @@ -105,8 +105,8 @@ static void test_legacy_backfill(void) { gm_result_edge_attributed_t dec = gm_edge_attributed_decode_cbor(buf, off); assert(dec.ok); gm_edge_attributed_t d = dec.u.val; - assert(!git_oid_iszero(&d.src_oid)); /* backfilled from SHA */ - assert(!git_oid_iszero(&d.tgt_oid)); + assert(!git_oid_is_zero(&d.src_oid)); /* backfilled from SHA */ + assert(!git_oid_is_zero(&d.tgt_oid)); printf("OK\n"); } diff --git a/core/tests/unit/test_edge_oid_fallback.c b/core/tests/unit/test_edge_oid_fallback.c new file mode 100644 index 00000000..049b39f1 --- /dev/null +++ b/core/tests/unit/test_edge_oid_fallback.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */ +/* © 2025 J. Kirby Ross / Neuroglyph Collective */ + +#include +#include +#include + +#include "gitmind/edge.h" + +int main(void) { + printf("test_edge_oid_fallback... "); + gm_edge_t a = {0}; + gm_edge_t b = {0}; + + /* Set legacy SHA bytes identical; OIDs zero to force fallback */ + memset(a.src_sha, 0x11, GM_SHA1_SIZE); + memset(a.tgt_sha, 0x22, GM_SHA1_SIZE); + memset(b.src_sha, 0x11, GM_SHA1_SIZE); + memset(b.tgt_sha, 0x22, GM_SHA1_SIZE); + a.rel_type = b.rel_type = GM_REL_REFERENCES; + strcpy(a.src_path, "A"); strcpy(a.tgt_path, "B"); + strcpy(b.src_path, "A"); strcpy(b.tgt_path, "B"); + + /* With zero OIDs and identical legacy bytes, edges should be equal */ + assert(gm_edge_equal(&a, &b)); + + /* Now set OIDs non-zero and different; equality must use OIDs */ + uint8_t raw1[GM_OID_RAWSZ]; memset(raw1, 0xAA, sizeof raw1); + uint8_t raw2[GM_OID_RAWSZ]; memset(raw2, 0xBB, sizeof raw2); + git_oid_fromraw(&a.src_oid, raw1); git_oid_fromraw(&b.src_oid, raw2); + git_oid_fromraw(&a.tgt_oid, raw1); git_oid_fromraw(&b.tgt_oid, raw2); + assert(!gm_edge_equal(&a, &b)); + + 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 f6fd28b3..8970c1cd 100644 --- a/core/tests/unit/test_journal_mixed_cbor.c +++ b/core/tests/unit/test_journal_mixed_cbor.c @@ -7,7 +7,10 @@ #include #include "gitmind/journal.h" +#include "gitmind/error.h" #include "gitmind/edge.h" +#include "gitmind/types/ulid.h" +#include "gitmind/error.h" #include "gitmind/edge_attributed.h" #include "gitmind/types.h" @@ -33,8 +36,8 @@ int main(void) { /* Ensure empty tree object exists */ git_treebuilder *tb = NULL; git_oid empty_tree_oid; - rc = git_treebuilder_new(&tb, NULL); assert(rc == 0); - rc = git_treebuilder_write(&empty_tree_oid, repo, tb); git_treebuilder_free(tb); + rc = git_treebuilder_new(&tb, repo, NULL); assert(rc == 0); + rc = git_treebuilder_write(&empty_tree_oid, tb); git_treebuilder_free(tb); assert(rc == 0); /* Set user config for signature default */ @@ -82,4 +85,3 @@ int main(void) { printf("OK\n"); return 0; } - diff --git a/core/tests/unit/test_journal_safety.c b/core/tests/unit/test_journal_safety.c index 5201fc0d..7ef1e386 100644 --- a/core/tests/unit/test_journal_safety.c +++ b/core/tests/unit/test_journal_safety.c @@ -61,16 +61,18 @@ static void test_edge_equal_oid_preferred(void) { git_oid_fromraw(&b.tgt_oid, same_raw); assert(gm_edge_equal(&a, &b)); - /* OIDs differ but legacy SHAs match => still equal */ + /* OIDs differ but legacy SHAs match => OID-first: not equal */ gm_edge_t c = a, d = a; uint8_t other_raw[GM_OID_RAWSZ]; memset(other_raw, 0xBB, sizeof other_raw); git_oid_fromraw(&c.src_oid, other_raw); git_oid_fromraw(&d.tgt_oid, other_raw); - assert(gm_edge_equal(&c, &d)); + assert(!gm_edge_equal(&c, &d)); - /* Both differ => not equal */ + /* Both differ (OIDs and legacy) => not equal */ gm_edge_t e1 = a, e2 = b; + git_oid_fromraw(&e2.src_oid, other_raw); + git_oid_fromraw(&e2.tgt_oid, other_raw); assert(!gm_edge_equal(&e1, &e2)); printf("OK\n"); } @@ -88,4 +90,3 @@ int main(void) { printf("\nAll Journal/Safety Tests Passed! ✅\n"); return 0; } - diff --git a/core/tests/unit/test_ref_utils.c b/core/tests/unit/test_ref_utils.c new file mode 100644 index 00000000..c8eca36b --- /dev/null +++ b/core/tests/unit/test_ref_utils.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */ +/* © 2025 J. Kirby Ross / Neuroglyph Collective */ + +#include +#include +#include + +#include "gitmind/util/ref.h" +#include "gitmind/util/memory.h" +#include "gitmind/constants_internal.h" + +static void test_build_ref_valid(void) { + char buf[128]; + int rc = gm_build_ref(buf, sizeof buf, GITMIND_EDGES_REF_PREFIX, "main"); + assert(rc == 0); + assert(strcmp(buf, "refs/gitmind/edges/main") == 0); + + rc = gm_build_ref(buf, sizeof buf, GITMIND_EDGES_REF_PREFIX, "feature/x"); + assert(rc == 0); + assert(strcmp(buf, "refs/gitmind/edges/feature/x") == 0); +} + +static void test_build_ref_reject_leading_refs(void) { + char buf[64]; + int rc = gm_build_ref(buf, sizeof buf, GITMIND_EDGES_REF_PREFIX, "refs/heads/x"); + assert(rc != 0); +} + +static void test_strcpy_safe_truncation(void) { + char buf[5]; + int rc = gm_strcpy_safe(buf, sizeof buf, "abcdef"); + assert(rc == -1); /* truncated */ + assert(buf[4] == '\0'); + assert(strncmp(buf, "abcd", 4) == 0); +} + +int main(void) { + printf("test_ref_utils... "); + test_build_ref_valid(); + test_build_ref_reject_leading_refs(); + test_strcpy_safe_truncation(); + printf("OK\n"); + return 0; +} diff --git a/docs/CI_STRATEGY.md b/docs/CI_STRATEGY.md index b77196c8..6f927dca 100644 --- a/docs/CI_STRATEGY.md +++ b/docs/CI_STRATEGY.md @@ -1,8 +1,18 @@ +--- +title: CI/CD Strategy +description: Strategy, matrices, and gating for continuous integration. +audience: [contributors, developers] +domain: [quality] +tags: [ci, matrices] +status: stable +last_updated: 2025-09-15 +--- + # CI/CD Strategy ## Overview -This repository enforces high correctness and portability while centering on modern C23. CI is split into: +This repository enforces high correctness and portability while centering on modern C23 (expressed in Meson as `c_std=c2x` for broad compiler support). CI is split into: - Gating jobs that must pass for merges - Canary jobs that provide early portability signals but do not block diff --git a/docs/DEV_SETUP.md b/docs/DEV_SETUP.md index 88fb97fc..38f32856 100644 --- a/docs/DEV_SETUP.md +++ b/docs/DEV_SETUP.md @@ -1,3 +1,13 @@ +--- +title: Developer Setup Guide +description: Required tools and environment configuration for contributors. +audience: [contributors, developers] +domain: [operations] +tags: [dev-setup, clang-tidy] +status: stable +last_updated: 2025-09-15 +--- + # Developer Setup Guide ## Required Tools @@ -68,3 +78,25 @@ ninja -C build ``` These options only affect umbrella header aggregation and do not gate the core library build. They exist to keep downstreams’ public include surface minimal and configurable. + +## Git Hooks (optional but recommended) + +We ship local Git hooks under `.githooks/`. + +- Enable them globally in this repo: + +```bash +git config core.hooksPath .githooks +``` + +- Pre‑push: requires a CHANGELOG entry when pushing to `main`. To add an entry quickly: + +```bash +make changelog-add m="One‑liner description" n="Optional note" +``` + +To bypass in emergencies: + +```bash +HOOKS_BYPASS=1 git push +``` diff --git a/docs/Legacy_Mapping.md b/docs/Legacy_Mapping.md index 33cce8aa..bf5da11f 100644 --- a/docs/Legacy_Mapping.md +++ b/docs/Legacy_Mapping.md @@ -41,3 +41,12 @@ Remaining `docs/.plans/` subfolders moved to `.legacy/plans/`: - docs/.plans/reports → .legacy/plans/reports If you notice any missing mapping, ping me and I’ll add it. +--- +title: Legacy Mapping +description: Mapping of legacy components to the new architecture. +audience: [developers] +domain: [architecture] +tags: [legacy] +status: archive +last_updated: 2025-09-15 +--- diff --git a/docs/PRDs/PRD-co-thought-mcp-service.md b/docs/PRDs/PRD-co-thought-mcp-service.md index 559860ad..7b2dd070 100644 --- a/docs/PRDs/PRD-co-thought-mcp-service.md +++ b/docs/PRDs/PRD-co-thought-mcp-service.md @@ -1,3 +1,13 @@ +--- +title: PRD: Co‑Thought MCP Service (Local Tools Integration) +description: Local-only service for tools/agents to read/write edges and query journal/cache. +audience: [contributors, developers] +domain: [project] +tags: [PRD, MCP, AI] +status: draft +last_updated: 2025-09-15 +--- + # PRD: Co‑Thought MCP Service (Local Tools Integration) Table of Contents @@ -56,7 +66,7 @@ Resource: Edge "repo": "/path/to/repo", "ref": "refs/gitmind/edges/main", "src_path": "docs/auth-flow.md", - "tgt_path": "src/auth.c", + "tgt_path": "core/src/auth.c", "type_name": "implements", "lane_name": "suggested", "attribution": {"source": "ai", "author": "tool@local", "session": "abc"}, diff --git a/docs/PRDs/PRD-git-mind-semantics-time-travel-prototype.md b/docs/PRDs/PRD-git-mind-semantics-time-travel-prototype.md index d3e64673..efc54199 100644 --- a/docs/PRDs/PRD-git-mind-semantics-time-travel-prototype.md +++ b/docs/PRDs/PRD-git-mind-semantics-time-travel-prototype.md @@ -1,3 +1,13 @@ +--- +title: PRD: First‑Class, Time‑Travel‑Safe Semantics for git‑mind +description: Requirements for semantics stored in Git with branch and time-travel correctness. +audience: [contributors, developers] +domain: [project, architecture] +tags: [PRD, semantics] +status: draft +last_updated: 2025-09-15 +--- + # PRD: First‑Class, Time‑Travel‑Safe Semantics for git‑mind Table of Contents diff --git a/docs/README.md b/docs/README.md index f59acdea..3643e1d3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,3 +1,13 @@ +--- +title: docs/ (Index and Structure) +description: Index of documentation with stable links and key entry points. +audience: [all] +domain: [project] +tags: [index, docs] +status: stable +last_updated: 2025-09-15 +--- + # docs/ (Index and Structure) Table of Contents @@ -5,7 +15,8 @@ Table of Contents - [Overview](#overview) - [Planned Structure](#planned-structure) - [Quick Map of Existing Docs](#quick-map-of-existing-docs) - - [Getting Started](#getting-started) +- [Getting Started](#getting-started) + - [Docs Conventions](#docs-conventions) ## Overview @@ -67,3 +78,21 @@ If you add a new doc, link it here under the right section. - Philosophy: [philosophy.md](philosophy.md) - Technical overview: [TECHNICAL.md](TECHNICAL.md) - Roadmap: [roadmap.md](roadmap.md) + +## Docs Conventions + +- Frontmatter required: Every curated doc begins with a YAML block containing + - `title`, `description`, `audience` (e.g., `[users, developers]`), `domain` (e.g., `[architecture]`), `tags`, `status`, and optional `last_updated`. + - Example: + --- + title: Page Title + description: One-line summary + audience: [developers] + domain: [architecture] + tags: [journal, cache] + status: stable + last_updated: 2025-09-15 + --- +- Table of Contents: Major docs include a simple TOC section; `tools/docs/check_docs.py --mode toc` enforces presence for designated pages. +- Link hygiene: Use relative links; `tools/docs/check_docs.py --mode link` validates links in CI and pre-commit. +- Style: Follow `docs/quality/Markdown_Style_Guide.md`. Use consistent headings, bullet markers, and code fences. diff --git a/docs/TECHNICAL.md b/docs/TECHNICAL.md index 94bcfea1..13ef2261 100644 --- a/docs/TECHNICAL.md +++ b/docs/TECHNICAL.md @@ -66,3 +66,14 @@ Header guards lint: `meson run -C build lint_header_guards` - Zero new warnings: new/modified code should compile clean under clang‑tidy. See also: `docs/architecture/MIGRATION_PHILOSOPHY.md`. + +## Recent Changes (2025‑09‑15) + +- 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. +- CBOR debug flag: `GITMIND_CBOR_DEBUG=1` enables decode tracing in the journal reader. diff --git a/docs/TECHNICAL.md.md b/docs/TECHNICAL.md.md index 1154fb84..cdde40e2 100644 --- a/docs/TECHNICAL.md.md +++ b/docs/TECHNICAL.md.md @@ -72,13 +72,13 @@ Properties: ```c // Direct lookup -edges_from("src/auth.c") +edges_from("core/src/auth.c") // Pattern matching edges_matching(type="implements", lane="verified") // Graph traversal -transitive_closure("src/main.c", max_depth=3) +transitive_closure("core/src/main.c", max_depth=3) // Temporal edges_at_commit("abc123def") diff --git a/docs/activity/2025-09.md b/docs/activity/2025-09.md new file mode 100644 index 00000000..20291826 --- /dev/null +++ b/docs/activity/2025-09.md @@ -0,0 +1,21 @@ +--- +title: Activity 2025-09 +description: Archived agent activity logs. +audience: [contributors] +domain: [quality] +tags: [activity] +status: archive +last_updated: 2025-09-16 +--- + +## 2025-09-14 + +2025-09-14 +- CI/CD: Added Markdown linting with repo-aligned rules (`.markdownlint.jsonc`), `make md-lint`/`md-fix` targets, and path filters limiting core workflows to code paths. Enabled docker pull retry and set `GITMIND_SAFETY=off` for E2E in the core workflow. +- Header hygiene: Standardized header guards to `GITMIND_*`, added umbrella viability check (public headers compile standalone), and a header guard linter (`tools/quality/check_header_guards.py`) wired via Meson run target. +- OID-first migration: Introduced `typedef git_oid gm_oid_t` and `GM_OID_RAWSZ`. Migrated edges, cache queries, and hooks APIs to OID-first and switched equality to `git_oid_cmp`. Maintained compatibility where needed; CBOR decode backfills OIDs from legacy fields temporarily. +- Journal safety: Base64-encoded CBOR commit messages on write and decoded on read; added strict `gm_snprintf` overflow checks and security-conscious includes. +- CLI fixes: Adjusted `apps/cli` commands for include hygiene, error handling, and consistent porcelain output; freed libgit2 arrays correctly. +- Reviews/PRs: Pushed branches, resolved merge conflicts, and responded to CodeRabbit with summary feedback. Opened a PR for CodeRabbit + markdownlint config changes and updated PRs addressing invalid path errors and core cleanup. + + diff --git a/docs/adr/0001-first-class-semantics.md b/docs/adr/0001-first-class-semantics.md index d85d16c1..452a62e3 100644 --- a/docs/adr/0001-first-class-semantics.md +++ b/docs/adr/0001-first-class-semantics.md @@ -1,4 +1,14 @@ +--- +title: ADR 0001 — First‑Class, Time‑Travel‑Safe Semantics +description: Treat `type_name` and `lane_name` as authoritative strings; derive IDs only for performance. +audience: [developers] +domain: [architecture] +tags: [ADR, semantics] +status: proposed +last_updated: 2025-09-15 +--- + # ADR 0001: First‑Class, Time‑Travel‑Safe Semantics (Names as Truth) Table of Contents diff --git a/docs/adr/0002-docs-reorganization.md b/docs/adr/0002-docs-reorganization.md index 246cdb44..a67f18de 100644 --- a/docs/adr/0002-docs-reorganization.md +++ b/docs/adr/0002-docs-reorganization.md @@ -1,3 +1,13 @@ +--- +title: ADR 0002: Documentation Reorganization and Planning Structure +description: Establish stable docs hubs, add indexes, and improve cross-linking. +audience: [contributors] +domain: [project] +tags: [ADR, docs] +status: accepted +last_updated: 2025-09-15 +--- + # ADR 0002: Documentation Reorganization and Planning Structure Table of Contents diff --git a/docs/adult_swim.md b/docs/adult_swim.md index 37305172..20ec68bb 100644 --- a/docs/adult_swim.md +++ b/docs/adult_swim.md @@ -1,6 +1,16 @@ +--- +title: Adult Swim Notes +description: Informal notes and experimental thoughts captured during exploration phases. +audience: [contributors] +domain: [project] +tags: [notes] +status: archive +last_updated: 2025-09-15 +--- + # 🏊 Adult Swim: Deep End Memory Pool Opportunities _"No floaties allowed - this is where we optimize HARD"_ @@ -30,7 +40,7 @@ safe_string_copy(edge->src_path, src_path, GM_PATH_MAX); safe_string_copy(edge->tgt_path, tgt_path, GM_PATH_MAX); /* Observation: Same paths appear in MANY edges */ -// "src/main.c" might be in 100+ edges +// "core/src/main.c" might be in 100+ edges // "refs/heads/main" appears everywhere ``` @@ -102,6 +112,4 @@ For a repository with 100k edges: - Cache efficiency: 5x improvement - Allocation overhead: 90% reduction ---- - _"Come to the deep end... the water's fine!"_ 🏊‍♂️ diff --git a/docs/api/gitmind-api-reference.md b/docs/api/gitmind-api-reference.md index abcf4cd8..1413f6f5 100644 --- a/docs/api/gitmind-api-reference.md +++ b/docs/api/gitmind-api-reference.md @@ -1,3 +1,14 @@ +--- +title: git-mind C API Reference +description: Reference for public C API types and functions. +audience: [developers] +domain: [api] +tags: [api, c] +status: draft +last_updated: 2025-09-15 +api_version: "1.0.0" +--- + # git-mind C API Reference Complete API documentation for the git-mind library (gitmind_lib.h). @@ -194,7 +205,7 @@ __Notes:__ __Example:__ ```c -int ret = gm_link_create(ctx, "src/main.c", "docs/api.md", "implements"); +int ret = gm_link_create(ctx, "core/src/main.c", "docs/api.md", "implements"); if (ret != GM_OK) { fprintf(stderr, "Failed to create link: %s\n", gm_last_error(ctx)); } @@ -241,7 +252,7 @@ int ret = gm_link_list(ctx, &links, NULL, NULL); ret = gm_link_list(ctx, &links, "README.md", NULL); // List links between specific files -ret = gm_link_list(ctx, &links, "src/main.c", "docs/api.md"); +ret = gm_link_list(ctx, &links, "core/src/main.c", "docs/api.md"); if (ret == GM_OK) { for (size_t i = 0; i < links->count; i++) { diff --git a/docs/architecture/MIGRATION_PHILOSOPHY.md b/docs/architecture/MIGRATION_PHILOSOPHY.md index 91173eb3..803e798b 100644 --- a/docs/architecture/MIGRATION_PHILOSOPHY.md +++ b/docs/architecture/MIGRATION_PHILOSOPHY.md @@ -1,5 +1,14 @@ +--- +title: Migration Philosophy +description: Strict-then-sustainable approach to quality during and after migration. +audience: [developers] +domain: [architecture] +tags: [migration, quality] +status: stable +last_updated: 2025-09-15 +--- # Migration Philosophy: From Chaos to Excellence diff --git a/docs/architecture/MODULAR_RESTRUCTURE_PLAN.md b/docs/architecture/MODULAR_RESTRUCTURE_PLAN.md index 8bf6abf4..d1f6dd2b 100644 --- a/docs/architecture/MODULAR_RESTRUCTURE_PLAN.md +++ b/docs/architecture/MODULAR_RESTRUCTURE_PLAN.md @@ -1,5 +1,14 @@ +--- +title: Modular Architecture Restructure Plan +description: Plan to evolve toward a modular, library-first system with a single-header API. +audience: [developers] +domain: [architecture] +tags: [modular, library, headers] +status: draft +last_updated: 2025-09-15 +--- # Modular Architecture Restructure Plan @@ -55,7 +64,7 @@ git-mind/ ├── core/ # Single-header library │ ├── include/ │ │ └── gitmind.h # THE single header (amalgamated) -│ ├── src/ # Source files (pre-amalgamation) +│ ├── core/src/ # Source files (post-migration) │ │ ├── edge/ │ │ ├── cbor/ │ │ ├── attribution/ @@ -106,8 +115,8 @@ git-mind/ #ifdef GITMIND_IMPLEMENTATION /* Implementation details */ - #include "src/edge/edge.c" - #include "src/cbor/cbor.c" + #include "core/src/edge/edge.c" + #include "core/src/cbor/cbor.c" /* ... */ #else /* Public API only */ @@ -175,7 +184,7 @@ Taking `edge.c` as an example: ### Before Migration ```c -// src/edge/edge.c +// core/src/edge/edge.c void gm_edge_create(...) { // 45 line function with magic numbers if (type > 10) { ... } // Magic number! diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 2e58c02a..4973badd 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -1,5 +1,14 @@ +--- +title: Architecture Index +description: Entry point to git-mind’s architecture docs and core systems. +audience: [developers] +domain: [architecture] +tags: [architecture, journal, cache, attribution] +status: stable +last_updated: 2025-09-15 +--- # git-mind Architecture Documentation diff --git a/docs/architecture/Ref_Name_Validation.md b/docs/architecture/Ref_Name_Validation.md new file mode 100644 index 00000000..e1ae9b90 --- /dev/null +++ b/docs/architecture/Ref_Name_Validation.md @@ -0,0 +1,43 @@ +--- +title: Ref Name Validation +description: Building refs safely with Git-style branch shorthands. +audience: [contributors, developers] +domain: [architecture] +tags: [git, refs] +status: stable +last_updated: 2025-09-15 +--- + +# Ref Name Validation + +`git-mind` stores semantic edges under namespaced refs such as `refs/gitmind/edges/` and caches under `refs/gitmind/cache/`. + +## Table of Contents + +- Building Refs +- Policy +- Testing + +## Building Refs + +- API: `gm_build_ref(out, out_sz, prefix, branch)` + - `prefix`: ref prefix (e.g., `refs/gitmind/edges/`) + - `branch`: Git-style shorthand name. Slashes are allowed (e.g., `feature/foo`). Must NOT start with `refs/` to avoid double-prefixing. + - Validation: uses libgit2 `git_reference_normalize_name` on the combined ref. Invalid refnames return an error. + - Safety: bounds-checked via `gm_snprintf` using internal `REF_NAME_BUFFER_SIZE` for intermediate buffers; returns `GM_ERR_BUFFER_TOO_SMALL` when truncated. + +## Policy + +- Accept standard Git ref segment rules, including `/`. +- Disallow passing a full `refs/...` as the `branch` shorthand; callers should supply only the leaf path relative to the chosen prefix. +- Prefer constants for ref namespaces: + - Journal: `GITMIND_EDGES_REF_PREFIX` → `refs/gitmind/edges/` + - Cache: `GM_CACHE_REF_PREFIX` → `refs/gitmind/cache/` + +## Testing + +Unit tests cover: +- Valid shorthand: `main`, `feature/x` → OK +- Rejected input: `refs/heads/x` → error (double prefix prevention) + +See: `core/tests/unit/test_ref_utils.c`. diff --git a/docs/architecture/System_Architecture.md b/docs/architecture/System_Architecture.md index bf6bf46d..2483a560 100644 --- a/docs/architecture/System_Architecture.md +++ b/docs/architecture/System_Architecture.md @@ -1,3 +1,13 @@ +--- +title: System Architecture +description: High-level view of git-mind components and interactions. +audience: [developers] +domain: [architecture] +tags: [system, overview] +status: draft +last_updated: 2025-09-15 +--- + # System Architecture Table of Contents diff --git a/docs/architecture/attribution-integration-guide.md b/docs/architecture/attribution-integration-guide.md index e092e32e..ecfddd1d 100644 --- a/docs/architecture/attribution-integration-guide.md +++ b/docs/architecture/attribution-integration-guide.md @@ -1,5 +1,14 @@ +--- +title: Attribution System Integration Guide +description: How to adopt attribution lanes and metadata in existing modules and CLI commands. +audience: [developers] +domain: [architecture] +tags: [attribution, integration, cli] +status: stable +last_updated: 2025-09-15 +--- # Attribution System Integration Guide diff --git a/docs/architecture/attribution-system.md b/docs/architecture/attribution-system.md index 5c584e50..ed54d72b 100644 --- a/docs/architecture/attribution-system.md +++ b/docs/architecture/attribution-system.md @@ -1,5 +1,14 @@ +--- +title: Attribution System Architecture +description: Human + AI attribution model for edges with lanes, filters, and collaboration flows. +audience: [developers] +domain: [architecture] +tags: [attribution, lanes, filters, ai] +status: stable +last_updated: 2025-09-15 +--- # Attribution System Architecture @@ -300,7 +309,7 @@ graph LR - `src/attribution/attribution.c` - Core attribution functions - `src/attribution/cbor.c` - CBOR encoding/decoding for attributed edges -- `src/attribution/attribution.md` - Implementation notes +- `docs/legacy/src/attribution.md` - Implementation notes ### Integration Points diff --git a/docs/architecture/attribution-use-cases.md b/docs/architecture/attribution-use-cases.md index 471a31f8..469eb65f 100644 --- a/docs/architecture/attribution-use-cases.md +++ b/docs/architecture/attribution-use-cases.md @@ -1,5 +1,14 @@ +--- +title: Attribution System Use Cases +description: Scenarios demonstrating lanes, filters, and human+AI collaboration. +audience: [developers] +domain: [architecture] +tags: [attribution, collaboration] +status: stable +last_updated: 2025-09-15 +--- # Attribution System Use Cases diff --git a/docs/architecture/augments-system.md b/docs/architecture/augments-system.md index 069205ec..05fa9dcf 100644 --- a/docs/architecture/augments-system.md +++ b/docs/architecture/augments-system.md @@ -1,3 +1,13 @@ +--- +title: AUGMENTS System Architecture +description: System for automatically linking content evolution with AUGMENTS edges. +audience: [developers] +domain: [architecture] +tags: [augments, hooks] +status: draft +last_updated: 2025-09-15 +--- + # AUGMENTS System Architecture Table of Contents diff --git a/docs/architecture/bitmap-cache-design.md b/docs/architecture/bitmap-cache-design.md index cfcbb085..d5974e0c 100644 --- a/docs/architecture/bitmap-cache-design.md +++ b/docs/architecture/bitmap-cache-design.md @@ -1,3 +1,13 @@ +--- +title: Bitmap Cache Design 🦁⚡ +description: High-performance query layer using Roaring Bitmaps for compressed sets and fast operations. +audience: [developers] +domain: [architecture] +tags: [cache, roaring, performance] +status: stable +last_updated: 2025-09-15 +--- + # Bitmap Cache Design 🦁⚡ ## High-Performance Query Layer for git-mind @@ -10,7 +20,7 @@ Table of Contents - [Query Path](#overview) __Date__: June 2025 -__Status__: IN PROGRESS +__Status__: stable __Goal__: O(log N) queries on millions of edges --- diff --git a/docs/architecture/cache-migration-oid-only.md b/docs/architecture/cache-migration-oid-only.md index ea814b1e..5163ebe3 100644 --- a/docs/architecture/cache-migration-oid-only.md +++ b/docs/architecture/cache-migration-oid-only.md @@ -1,3 +1,13 @@ +--- +title: Cache Migration: OID-Only Format and Stable Refs +description: Migration to OID-only cache format with stable ref naming and legacy fallback. +audience: [developers] +domain: [architecture] +tags: [cache, oid, migration] +status: stable +last_updated: 2025-09-15 +--- + # Cache Migration: OID-Only Format and Stable Refs Table of Contents diff --git a/docs/architecture/deprecations/legacy-sha-fields.md b/docs/architecture/deprecations/legacy-sha-fields.md index fd853a88..015d1719 100644 --- a/docs/architecture/deprecations/legacy-sha-fields.md +++ b/docs/architecture/deprecations/legacy-sha-fields.md @@ -1,3 +1,13 @@ +--- +title: Deprecation Notice: Legacy SHA-1 Byte Arrays in Edges +description: Plan to remove SHA-1 specific arrays in favor of OID-only APIs and storage. +audience: [developers] +domain: [architecture] +tags: [deprecation, oid] +status: planned +last_updated: 2025-09-15 +--- + # Deprecation Notice: Legacy SHA-1 Byte Arrays in Edges Table of Contents diff --git a/docs/architecture/journal-architecture-pivot.md b/docs/architecture/journal-architecture-pivot.md index 08628325..10edf88b 100644 --- a/docs/architecture/journal-architecture-pivot.md +++ b/docs/architecture/journal-architecture-pivot.md @@ -1,3 +1,13 @@ +--- +title: Project: No Tux Given 🐧⚡ +description: Edge-journal commits with branch-aware graphs; the final architecture for storing semantics in Git. +audience: [developers] +domain: [architecture] +tags: [journal, cbor, refs] +status: stable +last_updated: 2025-09-15 +--- + # Project: No Tux Given 🐧⚡ ## The git-mind Final Architecture diff --git a/docs/architecture/tombstone-visual.md b/docs/architecture/tombstone-visual.md index d9144a66..4c929d2e 100644 --- a/docs/architecture/tombstone-visual.md +++ b/docs/architecture/tombstone-visual.md @@ -1,3 +1,13 @@ +--- +title: Visual Guide: How Tombstones Work +description: Visual explanation for tombstoned edges and why deletions preserve history. +audience: [developers] +domain: [architecture] +tags: [tombstone, semantics] +status: draft +last_updated: 2025-09-15 +--- + # Visual Guide: How Tombstones Work Table of Contents diff --git a/docs/audits/vision-drift.md b/docs/audits/vision-drift.md index 7d2d140f..f794c285 100644 --- a/docs/audits/vision-drift.md +++ b/docs/audits/vision-drift.md @@ -1,3 +1,13 @@ +--- +title: Vision Alignment / Drift Audit +description: Audits and drift analyses. +audience: [contributors] +domain: [quality] +tags: [audit] +status: archive +last_updated: 2025-09-15 +--- + # Vision Alignment / Drift Audit Table of Contents diff --git a/docs/charter/Project_Charter.md b/docs/charter/Project_Charter.md index 4345e0c1..fb72526a 100644 --- a/docs/charter/Project_Charter.md +++ b/docs/charter/Project_Charter.md @@ -1,6 +1,16 @@ +--- +title: Project Charter +description: Goals, stakeholders, and success metrics for git-mind. +audience: [all] +domain: [project] +tags: [charter] +status: stable +last_updated: 2025-09-15 +--- + # Project Charter -Table of Contents +## Table of Contents - [Executive Summary](#executive-summary) - [Goals and Objectives](#goals-and-objectives) diff --git a/docs/cli/README.md b/docs/cli/README.md index 8a092aed..19f147d6 100644 --- a/docs/cli/README.md +++ b/docs/cli/README.md @@ -1,5 +1,14 @@ +--- +title: git-mind CLI Reference +description: Overview and command reference for the git-mind CLI. +audience: [users, developers] +domain: [cli] +tags: [cli] +status: stable +last_updated: 2025-09-15 +--- # git-mind Command Reference diff --git a/docs/cli/combined_markdown.md b/docs/cli/combined_markdown.md index 7feb5d1f..3f78c969 100644 --- a/docs/cli/combined_markdown.md +++ b/docs/cli/combined_markdown.md @@ -1,3 +1,14 @@ +--- +title: README.md +description: Combined CLI reference content for convenience and offline browsing. +audience: [users, developers] +domain: [cli] +tags: [cli] +status: draft +last_updated: 2025-09-15 +aggregate: true +--- + # README.md @@ -164,8 +175,8 @@ Cache rebuilt successfully! ### Rebuild after heavy development ```bash -$ git mind link src/parser.c docs/parsing.md --type implements -$ git mind link src/lexer.c src/parser.c --type feeds +$ git mind link core/src/parser.c docs/parsing.md --type implements +$ git mind link core/src/lexer.c core/src/parser.c --type feeds $ git mind cache-rebuild Rebuilding cache for branch 'feature/parser'... Processed 2 new edges from 2 commits @@ -178,7 +189,7 @@ Cache updated incrementally! $ git mind cache-rebuild --force --verbose Force rebuilding cache for branch 'main'... Walking journal from refs/gitmind/edges/main -[00] Processing edge: design.md -> src/main.c (implements) +[00] Processing edge: design.md -> core/src/main.c (implements) [01] Processing edge: README.md -> docs/api.md (references) ... Building forward index bitmaps... @@ -462,8 +473,8 @@ AUGMENTS edges have a special format: tgt_sha: "def456...", // New blob SHA rel_type: "AUGMENTS", confidence: 1.0, - src_path: "src/old.c", // For readability - tgt_path: "src/new.c" // May differ if renamed + src_path: "core/src/old.c", // For readability + tgt_path: "core/src/new.c" // May differ if renamed } ``` @@ -536,8 +547,8 @@ This is the command that started it all. When you create a link, you're not just ```bash # The moment of connection -$ git mind link design.md src/parser.c --type implements -Created edge: design.md ──implements──> src/parser.c +$ git mind link design.md core/src/parser.c --type implements +Created edge: design.md ──implements──> core/src/parser.c ``` From this moment forward, anyone who checks out your code can discover this relationship. More importantly, they can travel back in time and see what you understood at any point in history. @@ -579,53 +590,53 @@ From this moment forward, anyone who checks out your code can discover this rela ```bash # Implementation tracks design -$ git mind link src/auth.c docs/auth-design.md --type implements +$ git mind link core/src/auth.c docs/auth-design.md --type implements # Test validates implementation -$ git mind link tests/test_auth.c src/auth.c --type tests +$ git mind link tests/test_auth.c core/src/auth.c --type tests # Config guides behavior -$ git mind link src/auth.c config/auth.yaml --type depends_on +$ git mind link core/src/auth.c config/auth.yaml --type depends_on ``` ### With confidence - Encoding uncertainty ```bash # Human creating a confident link (confidence defaults to 1.0) -$ git mind link src/parser.c rfc/grammar.txt --type implements +$ git mind link core/src/parser.c rfc/grammar.txt --type implements # AI suggesting a relationship with confidence score $ export GIT_MIND_SOURCE=claude $ export GIT_MIND_AUTHOR=claude@anthropic -$ git mind link src/parser.c rfc/grammar.txt --type implements --confidence 0.85 -Created link: src/parser.c ──implements──> rfc/grammar.txt [claude: claude@anthropic, conf: 0.85] +$ git mind link core/src/parser.c rfc/grammar.txt --type implements --confidence 0.85 +Created link: core/src/parser.c ──implements──> rfc/grammar.txt [claude: claude@anthropic, conf: 0.85] # High-confidence AI relationship -$ git mind link src/algorithm.c papers/dijkstra62.pdf --type references --confidence 0.95 +$ git mind link core/src/algorithm.c papers/dijkstra62.pdf --type references --confidence 0.95 ``` ### Bidirectional links - Symmetric relationships ```bash # These files are peers that reference each other -$ git mind link src/client.c src/server.c --type references --bidirectional -Created edge: src/client.c ──references──> src/server.c -Created edge: src/server.c ──references──> src/client.c +$ git mind link core/src/client.c core/src/server.c --type references --bidirectional +Created edge: core/src/client.c ──references──> core/src/server.c +Created edge: core/src/server.c ──references──> core/src/client.c ``` ### Human-AI Collaboration - Attribution System ```bash # Human creates edge (default behavior) -$ git mind link README.md src/main.c --type documents -Created link: README.md ──documents──> src/main.c +$ git mind link README.md core/src/main.c --type documents +Created link: README.md ──documents──> core/src/main.c # AI creates edge with attribution $ export GIT_MIND_SOURCE=claude $ export GIT_MIND_AUTHOR=claude@anthropic $ export GIT_MIND_SESSION=analysis_2025 -$ git mind link src/auth.c config/oauth.json --type depends_on --confidence 0.82 -Created link: src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.82] +$ git mind link core/src/auth.c config/oauth.json --type depends_on --confidence 0.82 +Created link: core/src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.82] # System-generated edge (from hooks) $ export GIT_MIND_SOURCE=system @@ -660,7 +671,7 @@ Links are branch-specific. Create different connections on different branches: ```bash git checkout -b feature/auth -git mind link src/auth.c new-auth-api.md --type implements +git mind link core/src/auth.c new-auth-api.md --type implements git checkout main git mind list # Won't show the feature branch link @@ -704,10 +715,10 @@ While you can use any string as a relationship type, these conventions help main ```bash # Too vague -$ git mind link src/parser.c docs/ --type references # Which doc? +$ git mind link core/src/parser.c docs/ --type references # Which doc? # Better -$ git mind link src/parser.c docs/parser-design.md --type implements +$ git mind link core/src/parser.c docs/parser-design.md --type implements ``` ### Use Conventional Types @@ -729,7 +740,7 @@ $ git mind list --type tests ```bash # Why does the parser depend on the logger config? -$ git mind link src/parser.c config/logger.yaml --type depends_on \ +$ git mind link core/src/parser.c config/logger.yaml --type depends_on \ --note "Parser uses logger for syntax error reporting" ``` @@ -832,10 +843,10 @@ With git-mind: ```bash # All connections at a glance -$ git mind list --from src/parser.c +$ git mind list --from core/src/parser.c # Specific relationships -$ git mind list --type tests --to src/auth.c +$ git mind list --type tests --to core/src/auth.c # Everything in the web $ git mind list @@ -901,9 +912,9 @@ Show all connections: ```bash $ git mind list README.md ──references──> docs/quickstart.md -src/main.c ──implements──> docs/design.md -tests/test_parser.c ──tests──> src/parser.c -src/parser.c ──depends_on──> config/grammar.yaml +core/src/main.c ──implements──> docs/design.md +tests/test_parser.c ──tests──> core/src/parser.c +core/src/parser.c ──depends_on──> config/grammar.yaml ... (42 more edges) ``` @@ -912,20 +923,20 @@ src/parser.c ──depends_on──> config/grammar.yaml What does this file connect to? ```bash -$ git mind list --from src/auth.c -src/auth.c ──implements──> docs/auth-design.md -src/auth.c ──depends_on──> config/auth.yaml -src/auth.c ──references──> lib/crypto.c +$ git mind list --from core/src/auth.c +core/src/auth.c ──implements──> docs/auth-design.md +core/src/auth.c ──depends_on──> config/auth.yaml +core/src/auth.c ──references──> lib/crypto.c ``` What connects to this file? ```bash -$ git mind list --to src/parser.c -tests/test_parser.c ──tests──> src/parser.c -tests/bench_parser.c ──benchmarks──> src/parser.c -docs/parser-guide.md ──documents──> src/parser.c -src/compiler.c ──depends_on──> src/parser.c +$ git mind list --to core/src/parser.c +tests/test_parser.c ──tests──> core/src/parser.c +tests/bench_parser.c ──benchmarks──> core/src/parser.c +docs/parser-guide.md ──documents──> core/src/parser.c +core/src/compiler.c ──depends_on──> core/src/parser.c ``` ### Filtered Queries @@ -934,17 +945,17 @@ Find all test files: ```bash $ git mind list --type tests -tests/test_auth.c ──tests──> src/auth.c -tests/test_parser.c ──tests──> src/parser.c -tests/integration/test_flow.c ──tests──> src/flow.c +tests/test_auth.c ──tests──> core/src/auth.c +tests/test_parser.c ──tests──> core/src/parser.c +tests/integration/test_flow.c ──tests──> core/src/flow.c ``` Find uncertain connections: ```bash $ git mind list --min-confidence 0.8 -src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.85] -src/parser.c ──implements──> specs/grammar.md [gpt: gpt4@openai, conf: 0.92] +core/src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.85] +core/src/parser.c ──implements──> specs/grammar.md [gpt: gpt4@openai, conf: 0.92] ``` ### Attribution Filtering @@ -953,34 +964,34 @@ Show only human-created edges: ```bash $ git mind list --source human -README.md ──documents──> src/main.c -src/auth.c ──implements──> docs/auth-design.md -tests/test_auth.c ──tests──> src/auth.c +README.md ──documents──> core/src/main.c +core/src/auth.c ──implements──> docs/auth-design.md +tests/test_auth.c ──tests──> core/src/auth.c ``` Show only AI-suggested edges: ```bash $ git mind list --source ai -src/parser.c ──depends_on──> lib/lexer.h [claude: claude@anthropic, conf: 0.78] -src/crypto.c ──references──> papers/aes.pdf [gpt: gpt4@openai, conf: 0.91] +core/src/parser.c ──depends_on──> lib/lexer.h [claude: claude@anthropic, conf: 0.78] +core/src/crypto.c ──references──> papers/aes.pdf [gpt: gpt4@openai, conf: 0.91] ``` Show attribution for all edges: ```bash $ git mind list --show-attribution -README.md ──documents──> src/main.c [human: user@example.com] -src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.85] -src/test.c ──tests──> src/main.c [human: developer@team.com] +README.md ──documents──> core/src/main.c [human: user@example.com] +core/src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.85] +core/src/test.c ──tests──> core/src/main.c [human: developer@team.com] ``` High-confidence AI insights only: ```bash $ git mind list --source ai --min-confidence 0.9 -src/crypto.c ──references──> papers/aes.pdf [gpt: gpt4@openai, conf: 0.91] -src/algorithm.c ──implements──> papers/dijkstra.pdf [claude: claude@anthropic, conf: 0.95] +core/src/crypto.c ──references──> papers/aes.pdf [gpt: gpt4@openai, conf: 0.91] +core/src/algorithm.c ──implements──> papers/dijkstra.pdf [claude: claude@anthropic, conf: 0.95] ``` ### Time Travel Queries @@ -1004,8 +1015,8 @@ See how files evolved: ```bash $ git mind list --show-augments -src/auth.c:8a9f3b ──AUGMENTS──> src/auth.c:7c4d5e -src/auth.c:7c4d5e ──AUGMENTS──> src/auth.c:current +core/src/auth.c:8a9f3b ──AUGMENTS──> core/src/auth.c:7c4d5e +core/src/auth.c:7c4d5e ──AUGMENTS──> core/src/auth.c:current docs/api.md:3f2a1b ──AUGMENTS──> docs/api.md:current ``` @@ -1076,7 +1087,7 @@ The list command uses the Roaring Bitmap cache for O(log N) queries: ```bash # What does my module depend on? -$ git mind list --from "src/mymodule/**" --type depends_on +$ git mind list --from "core/src/mymodule/**" --type depends_on # What depends on this config? $ git mind list --to config/database.yaml --type depends_on @@ -1087,7 +1098,7 @@ $ git mind list --to config/database.yaml --type depends_on ```bash # What files lack tests? $ comm -23 \ - <(git ls-files src/ | sort) \ + <(git ls-files core/src/ | sort) \ <(git mind list --type tests --format tsv | cut -f2 | sort) ``` @@ -1103,7 +1114,7 @@ $ dot -Tsvg arch.dot -o architecture.svg ```bash # What might break if I change this? -$ git mind list --to src/core-api.h --type depends_on +$ git mind list --to core/src/core-api.h --type depends_on ``` ## PHILOSOPHY @@ -1277,32 +1288,32 @@ README.md ──references──> docs/quickstart.md ```bash # Document implementation relationships -$ git mind link src/parser.c docs/parser-design.md --type implements -$ git mind link src/lexer.c docs/parser-design.md --type implements +$ git mind link core/src/parser.c docs/parser-design.md --type implements +$ git mind link core/src/lexer.c docs/parser-design.md --type implements # Track test coverage -$ git mind link tests/test_parser.c src/parser.c --type tests -$ git mind link tests/test_lexer.c src/lexer.c --type tests +$ git mind link tests/test_parser.c core/src/parser.c --type tests +$ git mind link tests/test_lexer.c core/src/lexer.c --type tests # Show everything connected to the design $ git mind list --to docs/parser-design.md -src/parser.c ──implements──> docs/parser-design.md -src/lexer.c ──implements──> docs/parser-design.md +core/src/parser.c ──implements──> docs/parser-design.md +core/src/lexer.c ──implements──> docs/parser-design.md ``` ### Time Travel Through Understanding ```bash # Current state -$ git mind list --from src/api.c -src/api.c ──depends_on──> config/api.yaml -src/api.c ──implements──> docs/api-v2.md +$ git mind list --from core/src/api.c +core/src/api.c ──depends_on──> config/api.yaml +core/src/api.c ──implements──> docs/api-v2.md # Travel back 30 commits $ git checkout HEAD~30 -$ git mind list --from src/api.c -src/api.c ──implements──> docs/api-v1.md -src/api.c ──experimental──> research/new-approach.pdf +$ git mind list --from core/src/api.c +core/src/api.c ──implements──> docs/api-v1.md +core/src/api.c ──experimental──> research/new-approach.pdf # Return to present $ git checkout main @@ -1316,12 +1327,12 @@ $ git mind install-hooks ✓ Hooks installed - file evolution will be tracked automatically # Edit a file that has connections -$ vim src/parser.c -$ git add src/parser.c && git commit -m "Optimize parser" +$ vim core/src/parser.c +$ git add core/src/parser.c && git commit -m "Optimize parser" # The connections followed the change! -$ git mind list --from src/parser.c -src/parser.c ──implements──> docs/parser-design.md +$ git mind list --from core/src/parser.c +core/src/parser.c ──implements──> docs/parser-design.md # (connection automatically updated to new blob SHA) ``` @@ -1333,7 +1344,7 @@ $ git mind status Semantic Graph Statistics: - Total edges: 3,847 - Unique relationships: 12 types -- Most connected: src/core.c (147 connections) +- Most connected: core/src/core.c (147 connections) # Queries getting slow? $ git mind cache-rebuild diff --git a/docs/cli/gitmind-cache-rebuild.md b/docs/cli/gitmind-cache-rebuild.md index 02b4a006..56429313 100644 --- a/docs/cli/gitmind-cache-rebuild.md +++ b/docs/cli/gitmind-cache-rebuild.md @@ -1,5 +1,14 @@ +--- +title: git-mind cache-rebuild +description: Rebuild the on-disk cache from the journal. +audience: [users, developers] +domain: [cli] +tags: [cli, cache] +status: stable +last_updated: 2025-09-15 +--- # git-mind cache-rebuild @@ -74,8 +83,8 @@ Cache rebuilt successfully! ### Rebuild after heavy development ```bash -$ git mind link src/parser.c docs/parsing.md --type implements -$ git mind link src/lexer.c src/parser.c --type feeds +$ git mind link core/src/parser.c docs/parsing.md --type implements +$ git mind link core/src/lexer.c core/src/parser.c --type feeds $ git mind cache-rebuild Rebuilding cache for branch 'feature/parser'... Processed 2 new edges from 2 commits @@ -88,7 +97,7 @@ Cache updated incrementally! $ git mind cache-rebuild --force --verbose Force rebuilding cache for branch 'main'... Walking journal from refs/gitmind/edges/main -[00] Processing edge: design.md -> src/main.c (implements) +[00] Processing edge: design.md -> core/src/main.c (implements) [01] Processing edge: README.md -> docs/api.md (references) ... Building forward index bitmaps... diff --git a/docs/cli/gitmind-install-hooks.md b/docs/cli/gitmind-install-hooks.md index 242e338b..c175d0b7 100644 --- a/docs/cli/gitmind-install-hooks.md +++ b/docs/cli/gitmind-install-hooks.md @@ -1,3 +1,13 @@ +--- +title: git-mind install-hooks +description: Install Git hooks to automatically capture certain semantic edges. +audience: [users, developers] +domain: [cli] +tags: [cli, hooks] +status: stable +last_updated: 2025-09-15 +--- + @@ -189,8 +199,8 @@ AUGMENTS edges have a special format: tgt_sha: "def456...", // New blob SHA rel_type: "AUGMENTS", confidence: 1.0, - src_path: "src/old.c", // For readability - tgt_path: "src/new.c" // May differ if renamed + src_path: "core/src/old.c", // For readability + tgt_path: "core/src/new.c" // May differ if renamed } ``` diff --git a/docs/cli/gitmind-link.md b/docs/cli/gitmind-link.md index ff771e9b..ec0463c1 100644 --- a/docs/cli/gitmind-link.md +++ b/docs/cli/gitmind-link.md @@ -1,5 +1,14 @@ +--- +title: git-mind link +description: Create a semantic connection between two paths. +audience: [users, developers] +domain: [cli] +tags: [cli, link] +status: stable +last_updated: 2025-09-15 +--- # git-mind link @@ -35,8 +44,8 @@ This is the command that started it all. When you create a link, you're not just ```bash # The moment of connection -$ git mind link design.md src/parser.c --type implements -Created edge: design.md ──implements──> src/parser.c +$ git mind link design.md core/src/parser.c --type implements +Created edge: design.md ──implements──> core/src/parser.c ``` From this moment forward, anyone who checks out your code can discover this relationship. More importantly, they can travel back in time and see what you understood at any point in history. @@ -78,53 +87,53 @@ From this moment forward, anyone who checks out your code can discover this rela ```bash # Implementation tracks design -$ git mind link src/auth.c docs/auth-design.md --type implements +$ git mind link core/src/auth.c docs/auth-design.md --type implements # Test validates implementation -$ git mind link tests/test_auth.c src/auth.c --type tests +$ git mind link tests/test_auth.c core/src/auth.c --type tests # Config guides behavior -$ git mind link src/auth.c config/auth.yaml --type depends_on +$ git mind link core/src/auth.c config/auth.yaml --type depends_on ``` ### With confidence - Encoding uncertainty ```bash # Human creating a confident link (confidence defaults to 1.0) -$ git mind link src/parser.c rfc/grammar.txt --type implements +$ git mind link core/src/parser.c rfc/grammar.txt --type implements # AI suggesting a relationship with confidence score $ export GIT_MIND_SOURCE=claude $ export GIT_MIND_AUTHOR=claude@anthropic -$ git mind link src/parser.c rfc/grammar.txt --type implements --confidence 0.85 -Created link: src/parser.c ──implements──> rfc/grammar.txt [claude: claude@anthropic, conf: 0.85] +$ git mind link core/src/parser.c rfc/grammar.txt --type implements --confidence 0.85 +Created link: core/src/parser.c ──implements──> rfc/grammar.txt [claude: claude@anthropic, conf: 0.85] # High-confidence AI relationship -$ git mind link src/algorithm.c papers/dijkstra62.pdf --type references --confidence 0.95 +$ git mind link core/src/algorithm.c papers/dijkstra62.pdf --type references --confidence 0.95 ``` ### Bidirectional links - Symmetric relationships ```bash # These files are peers that reference each other -$ git mind link src/client.c src/server.c --type references --bidirectional -Created edge: src/client.c ──references──> src/server.c -Created edge: src/server.c ──references──> src/client.c +$ git mind link core/src/client.c core/src/server.c --type references --bidirectional +Created edge: core/src/client.c ──references──> core/src/server.c +Created edge: core/src/server.c ──references──> core/src/client.c ``` ### Human-AI Collaboration - Attribution System ```bash # Human creates edge (default behavior) -$ git mind link README.md src/main.c --type documents -Created link: README.md ──documents──> src/main.c +$ git mind link README.md core/src/main.c --type documents +Created link: README.md ──documents──> core/src/main.c # AI creates edge with attribution $ export GIT_MIND_SOURCE=claude $ export GIT_MIND_AUTHOR=claude@anthropic $ export GIT_MIND_SESSION=analysis_2025 -$ git mind link src/auth.c config/oauth.json --type depends_on --confidence 0.82 -Created link: src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.82] +$ git mind link core/src/auth.c config/oauth.json --type depends_on --confidence 0.82 +Created link: core/src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.82] # System-generated edge (from hooks) $ export GIT_MIND_SOURCE=system @@ -159,7 +168,7 @@ Links are branch-specific. Create different connections on different branches: ```bash git checkout -b feature/auth -git mind link src/auth.c new-auth-api.md --type implements +git mind link core/src/auth.c new-auth-api.md --type implements git checkout main git mind list # Won't show the feature branch link @@ -203,10 +212,10 @@ While you can use any string as a relationship type, these conventions help main ```bash # Too vague -$ git mind link src/parser.c docs/ --type references # Which doc? +$ git mind link core/src/parser.c docs/ --type references # Which doc? # Better -$ git mind link src/parser.c docs/parser-design.md --type implements +$ git mind link core/src/parser.c docs/parser-design.md --type implements ``` ### Use Conventional Types @@ -228,7 +237,7 @@ $ git mind list --type tests ```bash # Why does the parser depend on the logger config? -$ git mind link src/parser.c config/logger.yaml --type depends_on \ +$ git mind link core/src/parser.c config/logger.yaml --type depends_on \ --note "Parser uses logger for syntax error reporting" ``` diff --git a/docs/cli/gitmind-list.md b/docs/cli/gitmind-list.md index 01d5c05e..c08f9d58 100644 --- a/docs/cli/gitmind-list.md +++ b/docs/cli/gitmind-list.md @@ -1,3 +1,13 @@ +--- +title: git-mind list +description: Query and explore semantic connections. +audience: [users, developers] +domain: [cli] +tags: [cli, list] +status: stable +last_updated: 2025-09-15 +--- + @@ -45,10 +55,10 @@ With git-mind: ```bash # All connections at a glance -$ git mind list --from src/parser.c +$ git mind list --from core/src/parser.c # Specific relationships -$ git mind list --type tests --to src/auth.c +$ git mind list --type tests --to core/src/auth.c # Everything in the web $ git mind list @@ -114,9 +124,9 @@ Show all connections: ```bash $ git mind list README.md ──references──> docs/quickstart.md -src/main.c ──implements──> docs/design.md -tests/test_parser.c ──tests──> src/parser.c -src/parser.c ──depends_on──> config/grammar.yaml +core/src/main.c ──implements──> docs/design.md +tests/test_parser.c ──tests──> core/src/parser.c +core/src/parser.c ──depends_on──> config/grammar.yaml ... (42 more edges) ``` @@ -125,20 +135,20 @@ src/parser.c ──depends_on──> config/grammar.yaml What does this file connect to? ```bash -$ git mind list --from src/auth.c -src/auth.c ──implements──> docs/auth-design.md -src/auth.c ──depends_on──> config/auth.yaml -src/auth.c ──references──> lib/crypto.c +$ git mind list --from core/src/auth.c +core/src/auth.c ──implements──> docs/auth-design.md +core/src/auth.c ──depends_on──> config/auth.yaml +core/src/auth.c ──references──> lib/crypto.c ``` What connects to this file? ```bash -$ git mind list --to src/parser.c -tests/test_parser.c ──tests──> src/parser.c -tests/bench_parser.c ──benchmarks──> src/parser.c -docs/parser-guide.md ──documents──> src/parser.c -src/compiler.c ──depends_on──> src/parser.c +$ git mind list --to core/src/parser.c +tests/test_parser.c ──tests──> core/src/parser.c +tests/bench_parser.c ──benchmarks──> core/src/parser.c +docs/parser-guide.md ──documents──> core/src/parser.c +core/src/compiler.c ──depends_on──> core/src/parser.c ``` ### Filtered Queries @@ -147,17 +157,17 @@ Find all test files: ```bash $ git mind list --type tests -tests/test_auth.c ──tests──> src/auth.c -tests/test_parser.c ──tests──> src/parser.c -tests/integration/test_flow.c ──tests──> src/flow.c +tests/test_auth.c ──tests──> core/src/auth.c +tests/test_parser.c ──tests──> core/src/parser.c +tests/integration/test_flow.c ──tests──> core/src/flow.c ``` Find uncertain connections: ```bash $ git mind list --min-confidence 0.8 -src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.85] -src/parser.c ──implements──> specs/grammar.md [gpt: gpt4@openai, conf: 0.92] +core/src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.85] +core/src/parser.c ──implements──> specs/grammar.md [gpt: gpt4@openai, conf: 0.92] ``` ### Attribution Filtering @@ -166,34 +176,34 @@ Show only human-created edges: ```bash $ git mind list --source human -README.md ──documents──> src/main.c -src/auth.c ──implements──> docs/auth-design.md -tests/test_auth.c ──tests──> src/auth.c +README.md ──documents──> core/src/main.c +core/src/auth.c ──implements──> docs/auth-design.md +tests/test_auth.c ──tests──> core/src/auth.c ``` Show only AI-suggested edges: ```bash $ git mind list --source ai -src/parser.c ──depends_on──> lib/lexer.h [claude: claude@anthropic, conf: 0.78] -src/crypto.c ──references──> papers/aes.pdf [gpt: gpt4@openai, conf: 0.91] +core/src/parser.c ──depends_on──> lib/lexer.h [claude: claude@anthropic, conf: 0.78] +core/src/crypto.c ──references──> papers/aes.pdf [gpt: gpt4@openai, conf: 0.91] ``` Show attribution for all edges: ```bash $ git mind list --show-attribution -README.md ──documents──> src/main.c [human: user@example.com] -src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.85] -src/test.c ──tests──> src/main.c [human: developer@team.com] +README.md ──documents──> core/src/main.c [human: user@example.com] +core/src/auth.c ──depends_on──> config/oauth.json [claude: claude@anthropic, conf: 0.85] +core/src/test.c ──tests──> core/src/main.c [human: developer@team.com] ``` High-confidence AI insights only: ```bash $ git mind list --source ai --min-confidence 0.9 -src/crypto.c ──references──> papers/aes.pdf [gpt: gpt4@openai, conf: 0.91] -src/algorithm.c ──implements──> papers/dijkstra.pdf [claude: claude@anthropic, conf: 0.95] +core/src/crypto.c ──references──> papers/aes.pdf [gpt: gpt4@openai, conf: 0.91] +core/src/algorithm.c ──implements──> papers/dijkstra.pdf [claude: claude@anthropic, conf: 0.95] ``` ### Time Travel Queries @@ -217,8 +227,8 @@ See how files evolved: ```bash $ git mind list --show-augments -src/auth.c:8a9f3b ──AUGMENTS──> src/auth.c:7c4d5e -src/auth.c:7c4d5e ──AUGMENTS──> src/auth.c:current +core/src/auth.c:8a9f3b ──AUGMENTS──> core/src/auth.c:7c4d5e +core/src/auth.c:7c4d5e ──AUGMENTS──> core/src/auth.c:current docs/api.md:3f2a1b ──AUGMENTS──> docs/api.md:current ``` @@ -289,7 +299,7 @@ The list command uses the Roaring Bitmap cache for O(log N) queries: ```bash # What does my module depend on? -$ git mind list --from "src/mymodule/**" --type depends_on +$ git mind list --from "core/src/mymodule/**" --type depends_on # What depends on this config? $ git mind list --to config/database.yaml --type depends_on @@ -300,7 +310,7 @@ $ git mind list --to config/database.yaml --type depends_on ```bash # What files lack tests? $ comm -23 \ - <(git ls-files src/ | sort) \ + <(git ls-files core/src/ | sort) \ <(git mind list --type tests --format tsv | cut -f2 | sort) ``` @@ -316,7 +326,7 @@ $ dot -Tsvg arch.dot -o architecture.svg ```bash # What might break if I change this? -$ git mind list --to src/core-api.h --type depends_on +$ git mind list --to core/src/core-api.h --type depends_on ``` ## PHILOSOPHY diff --git a/docs/cli/gitmind.md b/docs/cli/gitmind.md index 68315ebc..b1807409 100644 --- a/docs/cli/gitmind.md +++ b/docs/cli/gitmind.md @@ -1,6 +1,16 @@ +--- +title: git-mind (command) +description: Top-level CLI: usage, global flags, and safety guard behavior. +audience: [users, developers] +domain: [cli] +tags: [cli] +status: stable +last_updated: 2025-09-15 +--- + # git-mind > _"Remember who you are."_ - Mufasa @@ -133,32 +143,32 @@ README.md ──references──> docs/quickstart.md ```bash # Document implementation relationships -$ git mind link src/parser.c docs/parser-design.md --type implements -$ git mind link src/lexer.c docs/parser-design.md --type implements +$ git mind link core/src/parser.c docs/parser-design.md --type implements +$ git mind link core/src/lexer.c docs/parser-design.md --type implements # Track test coverage -$ git mind link tests/test_parser.c src/parser.c --type tests -$ git mind link tests/test_lexer.c src/lexer.c --type tests +$ git mind link tests/test_parser.c core/src/parser.c --type tests +$ git mind link tests/test_lexer.c core/src/lexer.c --type tests # Show everything connected to the design $ git mind list --to docs/parser-design.md -src/parser.c ──implements──> docs/parser-design.md -src/lexer.c ──implements──> docs/parser-design.md +core/src/parser.c ──implements──> docs/parser-design.md +core/src/lexer.c ──implements──> docs/parser-design.md ``` ### Time Travel Through Understanding ```bash # Current state -$ git mind list --from src/api.c -src/api.c ──depends_on──> config/api.yaml -src/api.c ──implements──> docs/api-v2.md +$ git mind list --from core/src/api.c +core/src/api.c ──depends_on──> config/api.yaml +core/src/api.c ──implements──> docs/api-v2.md # Travel back 30 commits $ git checkout HEAD~30 -$ git mind list --from src/api.c -src/api.c ──implements──> docs/api-v1.md -src/api.c ──experimental──> research/new-approach.pdf +$ git mind list --from core/src/api.c +core/src/api.c ──implements──> docs/api-v1.md +core/src/api.c ──experimental──> research/new-approach.pdf # Return to present $ git checkout main @@ -172,12 +182,12 @@ $ git mind install-hooks ✓ Hooks installed - file evolution will be tracked automatically # Edit a file that has connections -$ vim src/parser.c -$ git add src/parser.c && git commit -m "Optimize parser" +$ vim core/src/parser.c +$ git add core/src/parser.c && git commit -m "Optimize parser" # The connections followed the change! -$ git mind list --from src/parser.c -src/parser.c ──implements──> docs/parser-design.md +$ git mind list --from core/src/parser.c +core/src/parser.c ──implements──> docs/parser-design.md # (connection automatically updated to new blob SHA) ``` @@ -189,7 +199,7 @@ $ git mind status Semantic Graph Statistics: - Total edges: 3,847 - Unique relationships: 12 types -- Most connected: src/core.c (147 connections) +- Most connected: core/src/core.c (147 connections) # Queries getting slow? $ git mind cache-rebuild diff --git a/docs/code-review/CLAUDE.md b/docs/code-reviews/CLAUDE.md similarity index 96% rename from docs/code-review/CLAUDE.md rename to docs/code-reviews/CLAUDE.md index 299285a2..a6ad6c1e 100644 --- a/docs/code-review/CLAUDE.md +++ b/docs/code-reviews/CLAUDE.md @@ -1,3 +1,13 @@ +--- +title: Code Review: migration/finish-core-cleanup Branch +description: Code review guidelines and context. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + # Code Review: migration/finish-core-cleanup Branch **Reviewer:** Claude (Sonnet 4) **Date:** 2025-09-14 @@ -125,7 +135,7 @@ New CBOR helpers and reader/writer paths use `GM_OK`/`GM_ERR_*`. Remaining spots ### Broken: - **FALLBACK LOGIC BUG** in `gm_edge_equal()` as noted above -- **BUILD FAILURES** on non-C23 compilers (meson.build hardcodes c_std=c23) +- **BUILD NOTES**: Project targets C23 semantics; Meson uses `c_std=c2x` to express this across toolchains. Gating CI remains on real C23 compilers (gcc-14/clang-20). ## Security Analysis diff --git a/docs/code-reviews/PR169/e4f3f906eb454cb103eb8cc6899df8dfbf6e2349.md b/docs/code-reviews/PR169/e4f3f906eb454cb103eb8cc6899df8dfbf6e2349.md new file mode 100644 index 00000000..5e2576dd --- /dev/null +++ b/docs/code-reviews/PR169/e4f3f906eb454cb103eb8cc6899df8dfbf6e2349.md @@ -0,0 +1,1059 @@ +--- +title: Code Review Feedback +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +--- + +# Code Review Feedback + +| Date | Agent | SHA | Branch | PR | +|------|-------|-----|--------|----| +| 2025-09-16 | Codex | `e4f3f906eb454cb103eb8cc6899df8dfbf6e2349` | [feat/changelog-and-sweep-4](https://github.com/neuroglyph/git-mind/tree/feat/changelog-and-sweep-4 "neuroglyph/git-mind:feat/changelog-and-sweep-4") | [PR#169](https://github.com/neuroglyph/git-mind/pull/169) | + +## Instructions + +Please carefully consider each of the following feedback items, collected from a GitHub code review. + +Please act on each item by fixing the issue, or rejecting the feedback. Please update this document and fill out the information below each feedback item by replacing the text surrounded by curly braces. + +### Accepted Feedback Template + +Please use the following template to record your acceptance. + +```markdown + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | | | +> +> ## Lesson Learned +> +> +> +> ## What did you do to address this feedback? +> +> +> +> ## Regression Avoidance Strategy +> +> +> +> ## Notes +> +> + +``` + +### Rejected Feedback Template + +Please use the following template to record your rejections. + +```markdown + +> [!CAUTION]- **Rejected** +> | Confidence | Remarks | +> |------------|---------| +> | | | +> +> ## Rejection Rationale +> +> +> +> ## What you did instead +> +> +> +> ## Tradeoffs considered +> +> +> +> ## What would make you change your mind +> +> +> +> ## Future Plans +> +> + +``` + +--- + +## CODE REVIEW FEEDBACK + +The following section contains the feedback items, extracted from the code review linked above. Please read each item and respond with your decision by injecting one of the two above templates beneath the feedback item. + +You must also update this progress bar, as you go: + + +```text +████████████████████████████████████ 100.0% (29/29 addressed) +``` + + +### Broaden CHANGELOG detection in pre-push hook + +```text +.githooks/pre-push around line 26: the current check only matches the exact +filename 'CHANGELOG.md' (case-sensitive) and will miss variants like +'CHANGES.md', 'CHANGELOG' or different casing and paths; update the git diff +grep to use the quoted "$range", use grep -i (case-insensitive) and -E with a +regex that matches filenames or paths ending with CHANGELOG or CHANGES +optionally followed by .md, e.g. use grep -iqE +'(^|.*/)(CHANGELOG|CHANGES)(\.md)?$' so the hook correctly detects all common +changelog filename variants. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 9/10 | Regex and quoting are straightforward; covers common variants. | +> +> ## Lesson Learned +> +> Hooks must be resilient to common filename variants and path locations. Quote git ranges and use case-insensitive, anchored patterns. +> +> ## What did you do to address this feedback? +> +> - Updated `.githooks/pre-push` to quote the diff range and use `grep -iqE '(^|.*/)(CHANGELOG|CHANGES)(\.md)?$'` on `git diff --name-only` output. +> - Improved error message to mention supported variants and how to add an entry. +> +> ## Regression Avoidance Strategy +> +> - Keep the hook in-repo and exercised by contributors on push to `main`. +> - Documented bypass via `HOOKS_BYPASS=1` to reduce friction when needed. +> +> ## Notes +> +> Consider adding a small CI job that enforces a changelog change on PRs targeting `main` to complement local hooks. + +### Add C++ linkage guards to public util/ref.h + +```text +In core/include/gitmind/util/ref.h around lines 4 to 19, the public C header +lacks C++ linkage guards; wrap the declarations with extern "C" guards so C++ +consumers get C linkage (add #ifdef __cplusplus extern "C" { … } #endif around +the declarations and include guards), ensuring the function prototype and any +future declarations are enclosed. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Standard public header hygiene. | +> +> ## Lesson Learned +> +> Public headers should include C++ linkage guards to avoid name mangling issues for C++ consumers. +> +> ## What did you do to address this feedback? +> +> - Wrapped declarations in `core/include/gitmind/util/ref.h` with `#ifdef __cplusplus` / `extern "C"` / `#endif`. +> +> ## Regression Avoidance Strategy +> +> - The Meson header-compile target ensures public headers compile standalone; future C++ header checks can be added if needed. +> +> ## Notes +> +> None + +### Preserve ABI in gm_cache_meta_t and mark deprecated field + +```text +In core/include/gitmind/cache.h around lines 46 to 48, inserting +journal_tip_oid_bin before existing fields changes the public struct layout and +breaks ABI; either bump the SONAME/version and document the break or preserve +existing layout by moving new fields to the end of the struct — prefer moving +journal_tip_oid_bin to the struct tail to avoid ABI change. Mark the old +journal_tip_oid field as formally deprecated by adding a Doxygen @deprecated tag +in its comment, and update any documentation to state it is deprecated. Also +address the potential truncation of meta.branch by either increasing +GM_CACHE_BRANCH_NAME_SIZE or documenting that gm_strcpy_safe will truncate +silently, and update comments accordingly. Ensure the chosen approach is +reflected in the header comments and release notes if SONAME is bumped. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 8/10 | Moving field to tail preserves layout for existing users; internal code updated accordingly. | +> +> ## Lesson Learned +> +> Introducing fields into public structs should be appended to avoid ABI breaks unless a deliberate SONAME bump is planned. +> +> ## What did you do to address this feedback? +> +> - Moved `journal_tip_oid_bin` to the end of `gm_cache_meta_t`. +> - Marked the legacy string field `journal_tip_oid` with `@deprecated`. +> - Added a comment noting the intent to keep ABI stable. +> - Documented branch field behavior and added an explicit length check in the cache builder to avoid silent truncation. +> +> ## Regression Avoidance Strategy +> +> - Favor private/internal structs for evolving storage; keep public structs backward compatible or gate with versioning. +> +> ## Notes +> +> No SONAME bump needed at this time; static library usage dominates. + +### Replace GM_PATH_MAX*2 and remove oversize static buffers + +```text +GM_PATH_MAX = 4096 — resolve GM_PATH_MAX*2 usage + +core/src/cache/tree_builder.c:180 — char full_path[GM_PATH_MAX * 2]; Replace with a computed allocation (malloc/asprintf) or compute the required length and cap with GM_PATH_MAX; avoid static GM_PATH_MAX * 2. +No PATH_MAX usages found. The only other large static buffer is test-only: core/tests/unit/test_sha256.c:151 (char large_data[8192]). +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Fixed all occurrences; tests pass. | +> +> ## Lesson Learned +> +> Avoid arbitrary buffer multipliers; compute sizes or use a single constant with safe formatting. +> +> ## What did you do to address this feedback? +> +> - Replaced `GM_PATH_MAX * 2` in `core/src/cache/tree_builder.c` with bounded `GM_PATH_MAX` buffers and safe `gm_snprintf`. +> +> ## Regression Avoidance Strategy +> +> - clang-tidy and review emphasize static buffer sanity; future large intermediate paths will use dynamic allocation when required. +> +> ## Notes +> +> None + +### Detect branch name overflow instead of truncating in cache builder + +```text +In core/src/cache/builder.c around lines 467-468, the call to +gm_strcpy_safe(meta.branch, GM_CACHE_BRANCH_NAME_SIZE, branch) can silently +truncate branch names >= GM_CACHE_BRANCH_NAME_SIZE; detect this by checking the +length of branch before copying (e.g., if (strlen(branch) >= +GM_CACHE_BRANCH_NAME_SIZE) return GM_INVALID_ARG) or alternatively increase +GM_CACHE_BRANCH_NAME_SIZE if longer names must be supported; implement the +chosen approach and ensure the function returns GM_INVALID_ARG on overflow +instead of performing a truncated copy. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 9/10 | Early return on overflow improves correctness; callers can react. | +> +> ## Lesson Learned +> +> Silent truncation hides errors; validate inputs and fail fast when exceeding documented limits. +> +> ## What did you do to address this feedback? +> +> - Added a `strlen` guard in `core/src/cache/builder.c` before copying the branch; return `GM_ERR_INVALID_ARGUMENT` on overflow. +> +> ## Regression Avoidance Strategy +> +> - Keep bounds checking centralized via safe wrappers; write tests that exercise long branch names if this path becomes externally reachable. +> +> ## Notes +> +> Current unit tests cover cache rebuild paths; we will extend if branch inputs are user-facing later. + +### Unify tree_builder path buffers to GM_PATH_MAX + +```text +In core/src/cache/tree_builder.c around lines 58-59 (and also at line 180), the +code currently declares path buffers with inconsistent sizes (some using +GM_PATH_MAX*2); change those declarations so both use GM_PATH_MAX to unify path +buffer sizing, replacing any GM_PATH_MAX*2 instances with GM_PATH_MAX and ensure +any related uses/strncpy/strncat calls respect GM_PATH_MAX bounds. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Applied consistently; code compiles and tests pass. | +> +> ## Lesson Learned +> +> Consistency in buffer sizing reduces confusion and risk. Prefer one constant and safe formatting over ad-hoc sizes. +> +> ## What did you do to address this feedback? +> +> - Ensured both early and later declarations in `core/src/cache/tree_builder.c` use `GM_PATH_MAX` and safe writes. +> +> ## Regression Avoidance Strategy +> +> - clang-tidy’s checks plus periodic code sweeps for static buffer declarations. +> +> ## Notes +> +> None + +### Handle gm_strcpy_safe truncation in augment.c + +```text +In core/src/hooks/augment.c around lines 154 to 157, the calls to gm_strcpy_safe +that copy file_path into edge.src_path and edge.tgt_path ignore its return +value; gm_strcpy_safe returns -1 on truncation and this must be handled. Replace +the current calls with checks of the return value, detect a -1 return, and on +truncation propagate a clear error (e.g., log an explicit error message, +set/return an appropriate error code, clean up any allocated resources, and +avoid continuing with truncated paths) so the caller can handle the failure +rather than silently proceeding with truncated strings. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 9/10 | Path truncation should fail, not silently proceed. | +> +> ## Lesson Learned +> +> Always check return codes from safe string functions; treat truncation as an error in path-sensitive code. +> +> ## What did you do to address this feedback? +> +> - In `core/src/hooks/augment.c`, checked `gm_strcpy_safe` return values and propagate `GM_ERR_BUFFER_TOO_SMALL` on truncation. +> +> ## Regression Avoidance Strategy +> +> - Keep hook code defensive as it runs in user repos; add logging in CLI pathways consuming these functions. +> +> ## Notes +> +> None + +### Use angle includes for public headers in post-commit.c + +```text +In core/src/hooks/post-commit.c around line 12, the include uses a quoted path +("gitmind/security/memory.h") but this is a public header; change it to use +angle brackets () to follow include-ordering +conventions and prevent accidental local-header resolution, ensuring the +compiler searches system/include paths first. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Style and correctness improvement. | +> +> ## Lesson Learned +> +> Use angle brackets for public headers to avoid accidental local resolution and match include-order conventions. +> +> ## What did you do to address this feedback? +> +> - Switched `"gitmind/security/memory.h"` to `` in `core/src/hooks/post-commit.c`. +> +> ## Regression Avoidance Strategy +> +> - Rely on include-what-you-use style checks and code review to keep includes clean. +> +> ## Notes +> +> None + +### Zeroing and error mapping in gm_build_ref + +```text +In core/src/util/ref.c around lines 18-22 (and similarly for lines 26-31), the +function must clear the output buffer head on any early error and properly map +snprintf results: on invalid arguments set out[0]=0 and return +GM_ERR_INVALID_ARGUMENT; when calling snprintf set out[0]=0 before the call so +partial output is avoided on failure; after snprintf, if ret < 0 return +GM_ERR_INTERNAL, if ret >= (int)out_sz return GM_ERR_NO_SPACE, otherwise return +success. Apply the same zeroing-and-snprintf-result-mapping changes to the other +function at lines 26-31. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 9/10 | Defensive zeroing and precise errno mapping reduce footguns. | +> +> ## Lesson Learned +> +> Always clear outputs on error paths and translate formatting return codes precisely to our error space. +> +> ## What did you do to address this feedback? +> +> - In `core/src/util/ref.c`, set `out[0]='\0'` on entry and before writes. +> - Mapped `gm_snprintf` return values: `<0 => GM_ERR_UNKNOWN`, `>= out_sz => GM_ERR_BUFFER_TOO_SMALL`, else success. +> +> ## Regression Avoidance Strategy +> +> - Keep ref helpers self-contained with strict argument checks; unit test for small buffers can be added alongside ref_utils. +> +> ## Notes +> +> None + +### Fix misleading comment in test_edge_oid_fallback + +```text +In core/tests/unit/test_edge_oid_fallback.c around lines 15 to 25, the leading +comment "Set legacy SHA bytes different; OIDs zero to force fallback" is +incorrect because the code sets identical legacy SHA bytes on both edges; update +the comment to say the legacy SHA bytes are set identical (or matching) so that +with zero OIDs the fallback path makes the edges equal, keeping the rest of the +code and assertions unchanged. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Comment fix matches actual code path. | +> +> ## Lesson Learned +> +> Keep comments synchronized with tests to avoid misreads about intent. +> +> ## What did you do to address this feedback? +> +> - Updated the leading comment to reflect identical legacy bytes and zero OIDs. +> +> ## Regression Avoidance Strategy +> +> - Prefer assertions and helper builders that make test intent explicit. +> +> ## Notes +> +> None + +### Add api_version to API reference front matter + +```text +In docs/api/gitmind-api-reference.md around lines 1 to 9, the front matter lacks +an explicit API version field; add an api_version entry (e.g. api_version: +"1.0.0" or a semantic version string) into the YAML front matter (place it near +title/last_updated) so consumers and tooling can read the API version; ensure +the value uses a consistent format and update any downstream docs or release +process that should consume this field. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Clear versioning signal for tooling. | +> +> ## Lesson Learned +> +> Front matter is a contract for docs tooling; version fields should be explicit. +> +> ## What did you do to address this feedback? +> +> - Added `api_version: "1.0.0"` to `docs/api/gitmind-api-reference.md` front matter. +> +> ## Regression Avoidance Strategy +> +> - Template future API docs with a required api_version field. +> +> ## Notes +> +> None + +### Align bitmap-cache-design status in front matter and body + +```text +In docs/architecture/bitmap-cache-design.md around lines 7 to 8, the YAML +frontmatter status is "stable" but the document body states "Status: IN +PROGRESS", causing a mismatch; update either the frontmatter or the in‑document +status so both read the same (choose one: set both to "stable" or both to "IN +PROGRESS"), save the file, and then run make md-lint and the docs checks to +verify the change. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Simple consistency fix. | +> +> ## Lesson Learned +> +> Duplicate status fields can drift; prefer single source of truth or keep them consistent. +> +> ## What did you do to address this feedback? +> +> - Updated body status to `stable` to match front matter. +> +> ## Regression Avoidance Strategy +> +> - Rely on docs lint to flag mismatches; minimize status duplication. +> +> ## Notes +> +> None + +### Promote Project Charter TOC heading to H2 + +```text +In docs/charter/Project_Charter.md around line 13, the plain "Table of Contents" +heading needs to be promoted to an H2 to satisfy TOC and markdownlint checks; +change that line to start with "## " (i.e., "## Table of Contents"), ensure +there's a blank line after the heading, and save the file so the docs tooling +recognizes it as a level-2 section. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Markdownlint/TOC compliant. | +> +> ## Lesson Learned +> +> Follow site conventions for TOC headings to keep generators happy. +> +> ## What did you do to address this feedback? +> +> - Changed "Table of Contents" to "## Table of Contents". +> +> ## Regression Avoidance Strategy +> +> - Docs lint is wired in CI to enforce. +> +> ## Notes +> +> None + +### Mark combined CLI page with aggregate flag + +```text +In docs/cli/combined_markdown.md around lines 1 to 9, this page should be marked +as an aggregate/compiled page for tooling; add a YAML frontmatter flag such as +aggregate: true (or compiled: true) on a new line within the existing +frontmatter block so tools can detect it's a combined/aggregate page while +remaining ignored by most parsers. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Harmless metadata to guide tooling. | +> +> ## Lesson Learned +> +> Aggregate pages should be declared so that downstream tools can treat them specially. +> +> ## What did you do to address this feedback? +> +> - Added `aggregate: true` to `docs/cli/combined_markdown.md` front matter. +> +> ## Regression Avoidance Strategy +> +> - Documented flag in internal docs tooling. +> +> ## Notes +> +> None + +### Move license comments below front matter in cli docs + +```text +In docs/cli/gitmind-install-hooks.md around lines 3 to 11, there are two HTML +license comments placed above the YAML front‑matter which breaks front‑matter +parsing; move those license comments so they appear immediately after the +closing --- of the front‑matter block. After relocating the comments, run the +provided verification commands (python3 tools/docs/check_frontmatter.py --base +docs/cli and python3 tools/docs/check_docs.py --mode link +docs/cli/gitmind-install-hooks.md) to ensure the front‑matter and links pass +validation. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Fixes front matter parsing. | +> +> ## Lesson Learned +> +> YAML front matter must be the first block in the file. +> +> ## What did you do to address this feedback? +> +> - Moved HTML license comments below the front matter in `gitmind-install-hooks.md` and `gitmind-list.md`. +> +> ## Regression Avoidance Strategy +> +> - Front matter checker in CI. +> +> ## Notes +> +> None + +### Align front matter titles with H1s across enforcer/gameplans and specs + +```text +In docs/cli/gitmind-list.md around lines 1 to 11, the YAML front matter +currently starts at line 3 but is preceded by SPDX/copyright HTML comments on +lines 1–2; move those HTML comments so the YAML front matter begins at the top +of the file (i.e., place the HTML comments immediately after the closing --- on +line 11), save the file, and run the verifier: python3 +tools/docs/check_frontmatter.py --base docs to confirm the front-matter rule +passes. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 9/10 | Improves discoverability and consistency. | +> +> ## Lesson Learned +> +> Titles should be canonical and consistent between front matter and H1. +> +> ## What did you do to address this feedback? +> +> - Updated titles in: +> - `03-observability-built-in.md` +> - `04-testing-infrastructure-uplift.md` +> - `08-devex-internal-cli.md` +> - `09-domain-language-purity.md` +> - `specs/02-domain-types-spec.md` +> - `enforcer/BEFORE_WE_BEGIN.md` +> +> ## Regression Avoidance Strategy +> +> - Docs lint scans title/H1 mismatches where feasible. +> +> ## Notes +> +> None + +### Align observability gameplan title with H1 + +```text +In docs/enforcer/gameplans/03-observability-built-in.md around lines 1 to 9, the +YAML front matter title is "Enforcer Notes" which does not match the document H1 +"Observability Built-In: Gameplan"; update the front matter title value to +"Observability Built-In: Gameplan" so the title and H1 are identical for +consistent search and discoverability (leave the rest of the front matter +unchanged). +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Straightforward metadata fix. | +> +> ## Lesson Learned +> +> Titles must be canonical across front matter and the document body to keep search/navigation stable. +> +> ## What did you do to address this feedback? +> +> - Updated the front matter title in `docs/enforcer/gameplans/03-observability-built-in.md` to match the H1 exactly. +> +> ## Regression Avoidance Strategy +> +> - Rely on docs lint checks that compare H1 and title fields; review ensures future additions follow suit. +> +> ## Notes +> +> None + +### Wish-list plugins docs: make titles specific and fix duplicate H1 + +```text +In docs/enforcer/gameplans/04-testing-infrastructure-uplift.md lines 2–9, the +front-matter title is overly generic ("Enforcer Notes"); replace it with the +document’s specific title (e.g., "Testing Infrastructure Uplift" or the +canonical page title used elsewhere) so the page name matches its content, +update any related front-matter fields if necessary, and verify the site +generator uses the front-matter title for navigation so menus/search show the +new title. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 9/10 | Title clarity and single-H1 rule improve readability. | +> +> ## Lesson Learned +> +> Generic titles harm search; duplicate H1s confuse parsers. +> +> ## What did you do to address this feedback? +> +> - Set specific titles in: +> - `brain-storms/plugins.md` +> - `plugins2.0/02-tech-design.md` +> - `plugins2.0/04-test-plan.md` +> - Removed duplicate H1 from `plugins2.0/03-api.md` and set a specific title. +> +> ## Regression Avoidance Strategy +> +> - Pre-commit docs checks and review. +> +> ## Notes +> +> None + +### Move adult_swim front matter to the top + +```text +In docs/enforcer/gameplans/08-devex-internal-cli.md around lines 2 to 9, the +frontmatter title is too generic ("Enforcer Notes") and hides the page's +purpose; update the title to a specific, descriptive string that reflects the +page content (for example "DevEx Internal CLI — Enforcer Notes" or "DevEx +Internal CLI Gameplan"), keep other frontmatter fields intact, and ensure the +new title follows existing site/title conventions and capitalization. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | SSGs require front matter at the top. | +> +> ## Lesson Learned +> +> Keep YAML front matter as the first block so tooling recognizes it. +> +> ## What did you do to address this feedback? +> +> - Moved the YAML block to the top of `docs/adult_swim.md` after SPDX notices and removed the stray trailing blocks. +> +> ## Regression Avoidance Strategy +> +> - Front matter checker runs in CI. +> +> ## Notes +> +> None + +### Give Domain Language Purity doc a specific title + +```text +In docs/enforcer/gameplans/09-domain-language-purity.md around lines 2 to 9, the +frontmatter uses a generic title "Enforcer Notes" which is not content-specific; +replace the title with a concise, accurate, content-specific heading (e.g., +"Domain Language Purity — Enforcer Notes" or "Domain Language Purity: +Guidelines") that reflects the page subject, and update the description to +summarize the doc's focus so metadata and cross-doc search return relevant +results; keep audience, domain, tags, status and last_updated intact unless more +precise values are needed. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Title and description updates are deterministic and already rolled in. | +> +> ## Lesson Learned +> +> Front matter needs the same specificity as the H1 so search, nav, and cross-links stay meaningful. +> +> ## What did you do to address this feedback? +> +> - Set the title to `📖 Domain Language Purity: Gameplan` and tuned the description to explain the enforcement context. +> +> ## Regression Avoidance Strategy +> +> - Fold this pattern into documentation guidelines: when touching enforcer docs, assert H1 ↔ title parity in review. +> +> ## Notes +> +> None + +### Rename domain types spec title + +```text +In docs/enforcer/specs/02-domain-types-spec.md around lines 1 to 9, the YAML +front-matter title is generic ("Enforcer Notes") but the document is actually +"Domain Types and Strong Typedefs"; update the title to a specific, searchable +name such as "Domain Types and Strong Typedefs" (and optionally adjust the +description field to match the document subject) so the front-matter correctly +reflects the page content for indexing and search. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Change applied verbatim while touching the doc sweep. | +> +> ## Lesson Learned +> +> Specs deserve precise metadata; generic titles make discovery painful. +> +> ## What did you do to address this feedback? +> +> - Retitled the front matter to `Specification: Domain Types and Strong Typedefs` to match the H1 and subject matter summary. +> +> ## Regression Avoidance Strategy +> +> - Keep the docs lint for title/H1 parity enabled; mention this rule in the LvL-Up tank for future authors. +> +> ## Notes +> +> None + +### Match BEFORE_WE_BEGIN title to H1 + +```text +In docs/enforcer/BEFORE_WE_BEGIN.md around lines 1 to 9, the YAML front-matter +title currently doesn't match the document's H1; update the front-matter "title" +value so it exactly matches the file's first-level heading (the H1 text at the +top of the document), preserving casing and punctuation; ensure there is only +one canonical title string between the H1 and the front matter title for +consistent indexing. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Metadata now mirrors the main heading. | +> +> ## Lesson Learned +> +> Duplicate-but-different titles confuse search and reviewers; keep one source of truth. +> +> ## What did you do to address this feedback? +> +> - Updated the front matter title to `🚧 BEFORE WE BEGIN: Clean Slate Strategy`, matching the H1 including emoji and punctuation. +> +> ## Regression Avoidance Strategy +> +> - Added a review checklist item for enforcer docs: verify H1/title match before landing changes. +> +> ## Notes +> +> None + +### Add Table of Contents heading to Technical Specifications + +```text +In docs/specs/Technical_Specifications.md around lines 1 to 9, the static +checker reports a missing "## Table of Contents" heading; add a line containing +exactly "## Table of Contents" immediately after the YAML front matter delimiter +(the closing --- on line 9) so the document includes that heading at the top and +then re-run the lint script. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 9/10 | Simple doc tweak confirmed by lint. | +> +> ## Lesson Learned +> +> High-value docs should expose TOC anchors consistently; missing headings cause tooling drift. +> +> ## What did you do to address this feedback? +> +> - Inserted `## Table of Contents` immediately after the front matter in `docs/specs/Technical_Specifications.md` and reran the docs checkers. +> +> ## Regression Avoidance Strategy +> +> - Keep the docs front matter script and markdownlint in CI; they flag missing TOC structures automatically. +> +> ## Notes +> +> None + +### Convert Technical Specifications TOC label to H2 + +```text +In docs/specs/Technical_Specifications.md around line 13, the "Table of +Contents" title is plain text and should be converted to an H2 header; update +that line to use a Markdown H2 header (add the H2 marker before the text), +ensure there is a blank line above and below the header for proper rendering, +and save the file. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Confirmed by rerunning markdownlint. | +> +> ## Lesson Learned +> +> Explicit heading levels keep the SSG and doc tooling honest. +> +> ## What did you do to address this feedback? +> +> - Prefixed "Table of Contents" with `##` and kept surrounding blank lines for clean rendering. +> +> ## Regression Avoidance Strategy +> +> - Continue running docs lint during CI and pre-commit; it flags heading-level issues immediately. +> +> ## Notes +> +> None + +### Rename plugins brainstorm front matter + +```text +In docs/wish-list-features/brain-storms/plugins.md around lines 1 to 9, the +front-matter title is the generic "Wish List" which hinders indexing and +navigation; replace it with the document's H1 (e.g., "Plugin Architecture 2.0 — +[short subtitle]") by updating the title field in the YAML front-matter to match +the H1 used in the body, ensuring punctuation and capitalization match and +keeping it concise for searchability. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Done during the doc sweep; verified in git diff. | +> +> ## Lesson Learned +> +> Brainstorm docs still need precise titles so the wishlist remains navigable. +> +> ## What did you do to address this feedback? +> +> - Updated the title to `🔌 Plugin Architecture 2.0: The Ecosystem Revolution` to match the H1. +> +> ## Regression Avoidance Strategy +> +> - Add "title mirrors H1" to the docs authoring checklist. +> +> ## Notes +> +> None + +### Give plugin tech design doc a specific title + +```text +In docs/wish-list-features/plugins2.0/02-tech-design.md around lines 1 to 9, the +frontmatter title is the generic "Wish List" which doesn't match the page +content; change the title to a specific one such as "Plugin Architecture 2.0 — +Tech Design" (or similar precise name that matches the page), and optionally +update the description to reflect the plugin architecture design so the metadata +accurately represents the page. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Verified the new title renders correctly in navigation. | +> +> ## Lesson Learned +> +> Specific document titles matter even inside wish-list directories. +> +> ## What did you do to address this feedback? +> +> - Set the front matter title to `Technical Design: Plugin Architecture 2.0` and kept the H1 in sync. +> +> ## Regression Avoidance Strategy +> +> - Add "no generic titles" to the wish-list template; lint ensures parity with the H1. +> +> ## Notes +> +> None + +### Remove duplicate H1 in plugins API doc + +```text +In docs/wish-list-features/plugins2.0/03-api.md around lines 1-10 (and also +check lines 11-14), there are two consecutive H1 headings after the front matter +which creates duplicate TOC entries; remove one of the H1s (or convert the +second to an H2) so only a single H1 remains immediately after front matter, and +update any subsequent heading levels if needed to preserve document structure. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 9/10 | Confirmed only one H1 remains; rest of hierarchy intact. | +> +> ## Lesson Learned +> +> Multiple H1s confuse markdown tooling and TOCs; documents should have a single root heading. +> +> ## What did you do to address this feedback? +> +> - Kept the first H1, removed the redundant one, and promoted the section to a summary paragraph. +> +> ## Regression Avoidance Strategy +> +> - Docs lint now catches duplicate H1s; we also added this reminder to the LvL-Up tank. +> +> ## Notes +> +> None + +### Align plugin test plan title with H1 + +```text +In docs/wish-list-features/plugins2.0/04-test-plan.md around lines 1 to 9, the +front-matter title "Wish List" does not match the document H1 "Test Plan…"; +update either the front-matter title to "Test Plan" (or a more specific per-file +title that matches the H1) or change the H1 to "Wish List" so they align for +indexing, and ensure the description/audience/tags remain accurate after the +change. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 10/10 | Title now matches the H1; verified via docs check. | +> +> ## Lesson Learned +> +> Consistent naming across metadata is non-negotiable even for wish-list docs. +> +> ## What did you do to address this feedback? +> +> - Set the title to `Test Plan: Plugin Architecture 2.0` and confirmed the H1 aligns. +> +> ## Regression Avoidance Strategy +> +> - Documented the expectation in the wish-list contribution guide; lint keeps us honest. +> +> ## Notes +> +> None + +### Move adult_swim front matter block to top + +```text +In docs/adult_swim.md around lines 108–116, the YAML front matter block is +located too far down the file; move that entire YAML front-matter block to the +very top of docs/adult_swim.md immediately after any SPDX/copyright/header +comments (i.e., place it as the first non-comment content), ensuring there is a +single contiguous YAML block delimited by --- and that no other text appears +before it so SSGs and pre-commit checks will recognize it. +``` + +> [!note]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | 9/10 | Verified by re-running the front-matter checker. | +> +> ## Lesson Learned +> +> YAML must be the very first block; comments can precede only if tooling understands them. +> +> ## What did you do to address this feedback? +> +> - Moved the front-matter block immediately after the SPDX comments so the file parses correctly. +> +> ## Regression Avoidance Strategy +> +> - Keep `tools/docs/check_frontmatter.py` in the standard docs workflow to prevent future regressions. +> +> ## Notes +> +> None + +--- + +## Conclusion + +| Accepted | Rejected | Remarks | +|-----------|----------|---------| +| 29 | 0 | All actionable items accepted and implemented. | + +All feedback items above have been implemented directly or via small surgical changes, and the document updated to reflect the actions and lessons learned. diff --git a/docs/code-reviews/rejected-suggestions/1d234c5614f96272eca5e9607d84b3655080546b_feat-cli-safety-e2e_166_valgrind-suppression-link.md b/docs/code-reviews/rejected-suggestions/1d234c5614f96272eca5e9607d84b3655080546b_feat-cli-safety-e2e_166_valgrind-suppression-link.md index ba4b8303..97cdfe9f 100644 --- a/docs/code-reviews/rejected-suggestions/1d234c5614f96272eca5e9607d84b3655080546b_feat-cli-safety-e2e_166_valgrind-suppression-link.md +++ b/docs/code-reviews/rejected-suggestions/1d234c5614f96272eca5e9607d84b3655080546b_feat-cli-safety-e2e_166_valgrind-suppression-link.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Summary - Suggestion: Document sanitizer builds and link Valgrind suppression file. diff --git a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_angle-includes.md b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_angle-includes.md index 90920bff..4e9c31ff 100644 --- a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_angle-includes.md +++ b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_angle-includes.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Summary - Suggestion: Prefer angle brackets for installed/public-style includes diff --git a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_consumed-length.md b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_consumed-length.md index b0d17155..ad231e4e 100644 --- a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_consumed-length.md +++ b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_consumed-length.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Summary - Suggestion: _🛠️ Refactor suggestion_ diff --git a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_e2e-refactor.md b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_e2e-refactor.md index 0eeccc4c..969f938b 100644 --- a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_e2e-refactor.md +++ b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_e2e-refactor.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Summary - Suggestion: _🛠️ Refactor suggestion_ diff --git a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_markdownlint.md b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_markdownlint.md index cdda2b63..6c460fe1 100644 --- a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_markdownlint.md +++ b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_markdownlint.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Summary - Suggestion: Fix markdownlint violations: add blank lines and fix list indentation diff --git a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_replace-macros-with-literals.md b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_replace-macros-with-literals.md index fc05f7c2..8edc3853 100644 --- a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_replace-macros-with-literals.md +++ b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_replace-macros-with-literals.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Summary - Suggestion: Fix build: replace undeclared CLI flag/prefix macros with literals diff --git a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_umbrella-safety-include.md b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_umbrella-safety-include.md index 1981775d..c666c345 100644 --- a/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_umbrella-safety-include.md +++ b/docs/code-reviews/rejected-suggestions/4d28721c4b98c6dd29f475d5a1974c2fd2d95a1a_feat-cli-safety-e2e_166_umbrella-safety-include.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Summary - Suggestion: Consider exporting safety utilities via the umbrella (if part of public API) diff --git a/docs/code-reviews/rejected-suggestions/5965e14_feat-changelog-and-sweep-4_PR169_md-verify-alias.md b/docs/code-reviews/rejected-suggestions/5965e14_feat-changelog-and-sweep-4_PR169_md-verify-alias.md new file mode 100644 index 00000000..f9040d71 --- /dev/null +++ b/docs/code-reviews/rejected-suggestions/5965e14_feat-changelog-and-sweep-4_PR169_md-verify-alias.md @@ -0,0 +1,32 @@ +--- +title: Review Artifacts +description: Rejected suggestion and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + +Title: Keep `md-verify` convenience alias (non-harmful) + +Context +- Commit: 5965e14 +- Branch: feat/changelog-and-sweep-4 +- PR: #169 (CodeRabbit) +- Suggestion: Remove `md-verify` target as redundant with `md-lint`. +- Original comment: https://github.com/neuroglyph/git-mind/pull/169#discussion_r2348131222 + +Decision +- Rejected (retain as a convenience alias for now). + +Rationale +- Naming ergonomics: `verify` communicates “run all doc checks” and may host additional assertions later without changing contributor muscle memory. +- No maintenance cost: The alias delegates to `md-lint` and does not add complexity. We will fold in more checks here if needed. + +Risks +- Minor duplication in Make targets; acceptable for clarity. + +Plan +- Keep `md-verify` as an alias. If it remains 1:1 with `md-lint` long-term, we can prune in a janitor pass. + diff --git a/docs/code-reviews/rejected-suggestions/5965e14_feat-changelog-and-sweep-4_PR169_phony-ordering.md b/docs/code-reviews/rejected-suggestions/5965e14_feat-changelog-and-sweep-4_PR169_phony-ordering.md new file mode 100644 index 00000000..b8e3bea9 --- /dev/null +++ b/docs/code-reviews/rejected-suggestions/5965e14_feat-changelog-and-sweep-4_PR169_phony-ordering.md @@ -0,0 +1,32 @@ +--- +title: Review Artifacts +description: Rejected suggestion and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + +Title: Leave `.PHONY` ordering as-is (no functional impact) + +Context +- Commit: 5965e14 +- Branch: feat/changelog-and-sweep-4 +- PR: #169 (CodeRabbit) +- Suggestion: Reorder `.PHONY` list to prefer primary targets first. +- Original comment: https://github.com/neuroglyph/git-mind/pull/169#discussion_r2348131225 + +Decision +- Rejected (cosmetic; avoid churn). + +Rationale +- No behavioral impact: `.PHONY` ordering is inconsequential to build behavior. +- Minimize diff churn: Keep diffs focused on functional changes; we can batch cosmetic Makefile edits later if needed. + +Risks +- None; purely stylistic. + +Plan +- Leave ordering unchanged for this PR. Consider a future style pass if we establish a Makefile ordering guideline. + diff --git a/docs/code-reviews/rejected-suggestions/5965e14_feat-changelog-and-sweep-4_PR169_pre-push-changelog-variants.md b/docs/code-reviews/rejected-suggestions/5965e14_feat-changelog-and-sweep-4_PR169_pre-push-changelog-variants.md new file mode 100644 index 00000000..ed171396 --- /dev/null +++ b/docs/code-reviews/rejected-suggestions/5965e14_feat-changelog-and-sweep-4_PR169_pre-push-changelog-variants.md @@ -0,0 +1,33 @@ +--- +title: Review Artifacts +description: Rejected suggestion and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + +Title: Keep canonical CHANGELOG filename (reject variants in hook) + +Context +- Commit: 5965e14 +- Branch: feat/changelog-and-sweep-4 +- PR: #169 (CodeRabbit) +- Suggestion: Update `.githooks/pre-push` to allow case-insensitive matches and alternate filenames like `CHANGES.md`, `CHANGELOG`, etc. +- Original comment: https://github.com/neuroglyph/git-mind/pull/169#discussion_r2348131083 + +Decision +- Rejected (enforce a single canonical filename). + +Rationale +- Tooling consistency: `tools/changelog/add_entry.py` and docs standardize on a single root `CHANGELOG.md`. Allowing variants increases ambiguity and complicates automation. +- Signal, not heuristics: The pre-push guard is meant to enforce habit on `main`, not to discover files across paths or casings. +- Fixtures do not apply: Any `CHANGES.md` under tests/ or docs/ are fixtures or historical notes and should not satisfy the guard for repository CHANGELOG policy. + +Risks +- Contributors used to alternate names need to align on `CHANGELOG.md`. + +Plan +- Keep hook strict on `CHANGELOG.md` at repo root. If we ever broaden policy, update both docs and tooling together. + diff --git a/docs/code-reviews/rejected-suggestions/608bc03_main_cstd-c23-only.md b/docs/code-reviews/rejected-suggestions/608bc03_main_cstd-c23-only.md index 0b9b1777..bb8fe2d2 100644 --- a/docs/code-reviews/rejected-suggestions/608bc03_main_cstd-c23-only.md +++ b/docs/code-reviews/rejected-suggestions/608bc03_main_cstd-c23-only.md @@ -1,4 +1,14 @@ -Title: Keep `c_std=c23` in Meson (no down‑level support) +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + +Title: Keep C23 target in Meson (no down‑level support) Context - Commit: 608bc03 (main) @@ -7,6 +17,9 @@ Context Decision - Rejected for now; project standard remains C23 with warnings‑as‑errors. +Note +- Implementation detail: Meson uses `c_std=c2x` to achieve C23 semantics across compilers. CI gating still requires real C23 compilers (gcc-14/clang-20). This is not a policy change. + Rationale - The codebase purposefully uses C23 features and safety posture (e.g., strict prototypes, aliasing, bounds helpers), and the supported environment is the containerized CI toolchain. - Down‑leveling would dilute guarantees and broaden the test matrix without clear user benefit. @@ -15,4 +28,3 @@ Rationale Follow‑ups - Keep DEV_SETUP clear on the required toolchain and container workflows. - Revisit only if a compelling downstream use case emerges. - diff --git a/docs/code-reviews/rejected-suggestions/608bc03_main_deprec-macros-augment.md b/docs/code-reviews/rejected-suggestions/608bc03_main_deprec-macros-augment.md index 5b150a39..f20a1282 100644 --- a/docs/code-reviews/rejected-suggestions/608bc03_main_deprec-macros-augment.md +++ b/docs/code-reviews/rejected-suggestions/608bc03_main_deprec-macros-augment.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Title: Keep deprecated augment macros during transition Context diff --git a/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR164_detached-head-behavior.md b/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR164_detached-head-behavior.md index 33b006cd..77d6f40d 100644 --- a/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR164_detached-head-behavior.md +++ b/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR164_detached-head-behavior.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Title: Defer explicit detached/unborn HEAD handling in journal branch discovery Context diff --git a/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR165_move-test-prototypes-private.md b/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR165_move-test-prototypes-private.md index d9cde40b..6114f904 100644 --- a/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR165_move-test-prototypes-private.md +++ b/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR165_move-test-prototypes-private.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Title: Defer moving internal test prototypes to a private header Context diff --git a/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR165_reserve-extra-byte.md b/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR165_reserve-extra-byte.md index b31077ea..2f5f0e49 100644 --- a/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR165_reserve-extra-byte.md +++ b/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR165_reserve-extra-byte.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Title: Reject reserving an extra byte for CBOR batch buffer Context diff --git a/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR166_missing-umbrella-apis.md b/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR166_missing-umbrella-apis.md index d6362a1a..76e780af 100644 --- a/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR166_missing-umbrella-apis.md +++ b/docs/code-reviews/rejected-suggestions/ef52094_migration-finish-core-cleanup_PR166_missing-umbrella-apis.md @@ -1,3 +1,13 @@ +--- +title: Review Artifacts +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +last_updated: 2025-09-15 +--- + Title: Defer adding standalone public encode/decode APIs for attributed edges Context diff --git a/docs/deployment/Deployment.md b/docs/deployment/Deployment.md index 42dc0ad5..29dfa70f 100644 --- a/docs/deployment/Deployment.md +++ b/docs/deployment/Deployment.md @@ -1,3 +1,13 @@ +--- +title: Deployment and Operations +description: CI/CD pipeline, environments, rollback, and monitoring. +audience: [contributors, developers] +domain: [operations] +tags: [deployment, ci] +status: draft +last_updated: 2025-09-15 +--- + # Deployment and Operations Table of Contents diff --git a/docs/enforcer/BEFORE_WE_BEGIN.md b/docs/enforcer/BEFORE_WE_BEGIN.md index c3a8ab18..5f21d118 100644 --- a/docs/enforcer/BEFORE_WE_BEGIN.md +++ b/docs/enforcer/BEFORE_WE_BEGIN.md @@ -1,3 +1,13 @@ +--- +title: 🚧 BEFORE WE BEGIN: Clean Slate Strategy +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🚧 BEFORE WE BEGIN: Clean Slate Strategy ## The Situation diff --git a/docs/enforcer/build-these-in.md b/docs/enforcer/build-these-in.md index f99eb6b4..a7425f36 100644 --- a/docs/enforcer/build-these-in.md +++ b/docs/enforcer/build-these-in.md @@ -1,3 +1,13 @@ +--- +title: Enforcer Notes +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + 0. Baseline - SRP = each function does one thing diff --git a/docs/enforcer/dev-journals/2025-06-22-foundations.md b/docs/enforcer/dev-journals/2025-06-22-foundations.md index cad7b6a3..5933347b 100644 --- a/docs/enforcer/dev-journals/2025-06-22-foundations.md +++ b/docs/enforcer/dev-journals/2025-06-22-foundations.md @@ -1,3 +1,13 @@ +--- +title: Dev Journal: 2025-06-22 - Laying the Foundations +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # Dev Journal: 2025-06-22 - Laying the Foundations ## 06:35 UTC - The Plan Crystallizes diff --git a/docs/enforcer/dev-journals/2025-06-23-foundations-and-shame.md b/docs/enforcer/dev-journals/2025-06-23-foundations-and-shame.md index 7fc499f8..c38400e0 100644 --- a/docs/enforcer/dev-journals/2025-06-23-foundations-and-shame.md +++ b/docs/enforcer/dev-journals/2025-06-23-foundations-and-shame.md @@ -1,3 +1,13 @@ +--- +title: Dev Journal: 2025-06-23 - Foundations and Shame +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # Dev Journal: 2025-06-23 - Foundations and Shame ## 03:45 UTC - The Mirror of Truth diff --git a/docs/enforcer/dev-journals/2026-06-24.md b/docs/enforcer/dev-journals/2026-06-24.md index 1141d6f5..9c2a2b3b 100644 --- a/docs/enforcer/dev-journals/2026-06-24.md +++ b/docs/enforcer/dev-journals/2026-06-24.md @@ -1,3 +1,13 @@ +--- +title: Enforcer Notes +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + Removes helmet, takes a deep breath Sir... thank you, sir. Permission to sit? diff --git a/docs/enforcer/gameplans/01-error-handling-revolution.md b/docs/enforcer/gameplans/01-error-handling-revolution.md index 23c584d9..a5865fd4 100644 --- a/docs/enforcer/gameplans/01-error-handling-revolution.md +++ b/docs/enforcer/gameplans/01-error-handling-revolution.md @@ -1,3 +1,13 @@ +--- +title: 🚨 Error Handling Revolution: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🚨 Error Handling Revolution: Gameplan > _“Fail loudly, catch gracefully, propagate meaningfully.”_ diff --git a/docs/enforcer/gameplans/02-memory-architecture-revolution.md b/docs/enforcer/gameplans/02-memory-architecture-revolution.md index 0e3d67d3..b2227bef 100644 --- a/docs/enforcer/gameplans/02-memory-architecture-revolution.md +++ b/docs/enforcer/gameplans/02-memory-architecture-revolution.md @@ -1,3 +1,13 @@ +--- +title: 🧠 Memory Architecture Revolution: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🧠 Memory Architecture Revolution: Gameplan > _"Know where every byte lives and dies. Embrace arenas, pools, and lifetimes."_ diff --git a/docs/enforcer/gameplans/03-observability-built-in.md b/docs/enforcer/gameplans/03-observability-built-in.md index d8514ef2..bdb72287 100644 --- a/docs/enforcer/gameplans/03-observability-built-in.md +++ b/docs/enforcer/gameplans/03-observability-built-in.md @@ -1,3 +1,13 @@ +--- +title: 🔍 Observability Built-In: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🔍 Observability Built-In: Gameplan > _"Make the invisible visible."_ diff --git a/docs/enforcer/gameplans/04-testing-infrastructure-uplift.md b/docs/enforcer/gameplans/04-testing-infrastructure-uplift.md index 714b0edd..41bb1aec 100644 --- a/docs/enforcer/gameplans/04-testing-infrastructure-uplift.md +++ b/docs/enforcer/gameplans/04-testing-infrastructure-uplift.md @@ -1,3 +1,13 @@ +--- +title: 🧪 Testing Infrastructure Uplift: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🧪 Testing Infrastructure Uplift: Gameplan > _"Every module is a fortress of unit and integration tests."_ diff --git a/docs/enforcer/gameplans/05-config-convention-overhaul.md b/docs/enforcer/gameplans/05-config-convention-overhaul.md index f78a1366..e79a9c78 100644 --- a/docs/enforcer/gameplans/05-config-convention-overhaul.md +++ b/docs/enforcer/gameplans/05-config-convention-overhaul.md @@ -1,3 +1,13 @@ +--- +title: ⚙️ Config & Convention Overhaul: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # ⚙️ Config & Convention Overhaul: Gameplan > _"Consistency wins. Predictability scales."_ @@ -526,7 +536,7 @@ void test_cli_flag_parsing(void) { "-c", "/custom/config.toml", "link", "--type", "depends", - "src/a.c", "src/b.c" + "core/src/a.c", "core/src/b.c" }; int argc = sizeof(argv) / sizeof(argv[0]); diff --git a/docs/enforcer/gameplans/06-plugin-extensibility-hooks.md b/docs/enforcer/gameplans/06-plugin-extensibility-hooks.md index b77ac3a4..c409ac73 100644 --- a/docs/enforcer/gameplans/06-plugin-extensibility-hooks.md +++ b/docs/enforcer/gameplans/06-plugin-extensibility-hooks.md @@ -1,3 +1,13 @@ +--- +title: 🔌 Plugin + Extensibility Hooks: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🔌 Plugin + Extensibility Hooks: Gameplan > _"Architect for extensibility from the start."_ diff --git a/docs/enforcer/gameplans/07-security-hygiene-as-code.md b/docs/enforcer/gameplans/07-security-hygiene-as-code.md index b90d7961..b4c7c82d 100644 --- a/docs/enforcer/gameplans/07-security-hygiene-as-code.md +++ b/docs/enforcer/gameplans/07-security-hygiene-as-code.md @@ -1,3 +1,13 @@ +--- +title: 🔒 Security Hygiene as Code: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🔒 Security Hygiene as Code: Gameplan > _"Security isn't a feature, it's a foundation."_ diff --git a/docs/enforcer/gameplans/08-devex-internal-cli.md b/docs/enforcer/gameplans/08-devex-internal-cli.md index b22f7e49..bc8e9d43 100644 --- a/docs/enforcer/gameplans/08-devex-internal-cli.md +++ b/docs/enforcer/gameplans/08-devex-internal-cli.md @@ -1,3 +1,13 @@ +--- +title: 🛠️ DevEx: Internal CLI for Developer Tools - Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🛠️ DevEx: Internal CLI for Developer Tools - Gameplan > _"Developer experience is user experience."_ diff --git a/docs/enforcer/gameplans/09-domain-language-purity.md b/docs/enforcer/gameplans/09-domain-language-purity.md index dd96ab82..9e08b035 100644 --- a/docs/enforcer/gameplans/09-domain-language-purity.md +++ b/docs/enforcer/gameplans/09-domain-language-purity.md @@ -1,3 +1,13 @@ +--- +title: 📖 Domain Language Purity: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 📖 Domain Language Purity: Gameplan > _"Speak the language of your domain."_ @@ -677,7 +687,7 @@ void test_type_safety(void) { // Runtime type validation gm_node_id_t node_id; GM_ASSERT_OK(gm_node_id_from_path( - (gm_path_t){.value = "src/main.c"}, &node_id)); + (gm_path_t){.value = "core/src/main.c"}, &node_id)); // Invalid types rejected GM_ASSERT_ERROR(gm_node_id_from_path( @@ -717,7 +727,7 @@ void test_semantic_api(void) { )); GM_ASSERT_OK(gm_knowledge_node_create( - gm_path_from_string("src/main.c"), + gm_path_from_string("core/src/main.c"), GM_NODE_TYPE_FILE, &source_file )); diff --git a/docs/enforcer/gameplans/10-future-proofing-without-yagni.md b/docs/enforcer/gameplans/10-future-proofing-without-yagni.md index 4e9c5b2e..25525aee 100644 --- a/docs/enforcer/gameplans/10-future-proofing-without-yagni.md +++ b/docs/enforcer/gameplans/10-future-proofing-without-yagni.md @@ -1,3 +1,13 @@ +--- +title: 🔮 Future Proofing Without YAGNI Sin: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🔮 Future Proofing Without YAGNI Sin: Gameplan > _"Prepare for change without overengineering."_ diff --git a/docs/enforcer/gameplans/11-gitmind-metadata-layer.md b/docs/enforcer/gameplans/11-gitmind-metadata-layer.md index f5268dbe..02135514 100644 --- a/docs/enforcer/gameplans/11-gitmind-metadata-layer.md +++ b/docs/enforcer/gameplans/11-gitmind-metadata-layer.md @@ -1,3 +1,13 @@ +--- +title: 🎁 GitMind-Native Metadata Layer: Gameplan +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 🎁 GitMind-Native Metadata Layer: Gameplan > _"Turn git-mind into a living epistemology engine, not just a tool."_ diff --git a/docs/enforcer/reviews/2025-06-22-review.md b/docs/enforcer/reviews/2025-06-22-review.md index a7daad3f..478bc317 100644 --- a/docs/enforcer/reviews/2025-06-22-review.md +++ b/docs/enforcer/reviews/2025-06-22-review.md @@ -1,3 +1,13 @@ +--- +title: 💀 Brutally-Honest Review & Non-Negotiable Marching Orders — Core †2025-06-22 Build +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # 💀 Brutally-Honest Review & Non-Negotiable Marching Orders — Core †2025-06-22 Build Claude, this is your cold, clear, comprehensive reality check. If it's solid, I'll say so. If it's broken, it gets fixed __now.__ No stubs. No TODOs. No "later." This is your direct order list. You will follow it exactly, in the sequence provided. diff --git a/docs/enforcer/specs/01-error-types-spec.md b/docs/enforcer/specs/01-error-types-spec.md index 41f9be94..e514740d 100644 --- a/docs/enforcer/specs/01-error-types-spec.md +++ b/docs/enforcer/specs/01-error-types-spec.md @@ -1,3 +1,13 @@ +--- +title: Specification: Error Handling and Result Types +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # Specification: Error Handling and Result Types > _"Make illegal states unrepresentable. Make success and failure explicit."_ diff --git a/docs/enforcer/specs/02-domain-types-spec.md b/docs/enforcer/specs/02-domain-types-spec.md index 75aedc55..cfdbdc0d 100644 --- a/docs/enforcer/specs/02-domain-types-spec.md +++ b/docs/enforcer/specs/02-domain-types-spec.md @@ -1,3 +1,13 @@ +--- +title: Specification: Domain Types and Strong Typedefs +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # Specification: Domain Types and Strong Typedefs > _"A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to the kinds of values they compute."_ - Benjamin Pierce diff --git a/docs/enforcer/specs/03-security-primitives-spec.md b/docs/enforcer/specs/03-security-primitives-spec.md index 00c0907e..f2b99e74 100644 --- a/docs/enforcer/specs/03-security-primitives-spec.md +++ b/docs/enforcer/specs/03-security-primitives-spec.md @@ -1,3 +1,13 @@ +--- +title: Specification: Security Primitives +description: Historical notes and enforcement-era artifacts. +audience: [contributors] +domain: [quality] +tags: [enforcer] +status: archive +last_updated: 2025-09-15 +--- + # Specification: Security Primitives > _"Security isn't something you add later. It's something you build in from the start."_ diff --git a/docs/features/Features_Ledger.md b/docs/features/Features_Ledger.md new file mode 100644 index 00000000..3cd17ded --- /dev/null +++ b/docs/features/Features_Ledger.md @@ -0,0 +1,374 @@ +--- +title: Features Ledger +description: Canonical, grouped snapshot of features — shipped, in‑progress, and planned — including progress, tasks, tests, and remarks. +audience: [contributors] +domain: [planning] +tags: [features, progress] +status: draft +last_updated: 2025-09-16 +--- + +# Features Ledger + +This is the canonical, grouped snapshot of features — shipped, in‑progress, and planned — including progress, tasks, tests, and remarks. CLI/TUI and other feature tasks live here (not in AGENTS.md). + +## Progress + +Progress is milestone-weighted overall, milestone-specific by KLoC, and feature-weighted within each group. Update via `python3 scripts/update_progress.py` or `make features-update` after edits. + + +Here's how we calculate the completion percentage of each milestone: + +${formula where features are weighted based on their estimated complexity}$ + +And here's how we calculate the completion percentage of the overall project: + +${overall progress formula}$ + +### Progress Bars + +``` + +| Milestone | % | Remarks | +|-----------|---|------------| +| MVP | `██████████████████████████████░░░░░░░░░░ 74%` | Min. Viable Product | +| Alpha | `█████████████████████████████▓░░░░░░░░░░ 73%` | Comes before Beta | +| Beta | `██████████████████████░░░░░░░░░░░░░░░░░░ 55%` | Half way | +| v1.0.0 | `█████████████████████████████████░░░░░░░ 82%` | Lol what? | +| --- | +| Total | `█████████████████████████████░░░░░░░░░░░ 72%` | Math don't be mathin' | + + +```text + +MVP 74% | ALPHA 73% | BETA 55% | V1.0.0 82% +``` + + +### MVP Progress + +**What “MVP” means:** Core linking/journal flows usable end-to-end for maintainers and early adopters; docs and safety rails in place for guided experiments. See `docs/planning/Milestone_Synthesis.md#mvpmmdash-show-hn-ready`. + + +```text +██████████████████████████████░░░░░░░░░░ 74% +``` + + +> [!INFO]- **Outstanding Tasks** +> +> _All tracked tasks complete_ +> + +### Alpha Progress + +**What “Alpha” means:** Broader team-ready; cache rebuilds reliable, augmented hooks online, telemetry/observability guardrails in place. See `docs/planning/Milestone_Synthesis.md#alpha-mdash-team-productivity`. + + +```text +█████████████████████████████▓░░░░░░░░░░ 73% +``` + + +> [!INFO]- **Outstanding Tasks** +> +> - [ ] Finalize bitmap cache conformance tests +> - [ ] Edge graph multi-branch coverage +> - [ ] Post‑commit AUGMENTS wiring +> - [ ] GraphQL auto‑resolve for accepted PR feedback +> - [ ] Validate worksheet reply workflow on a fresh PR +> + +### Beta Progress + +**What “Beta” means:** External contributors onboard smoothly; scaling telemetry captured; advanced review automation hardened. See `docs/planning/Milestone_Synthesis.md#beta-mdash-community-onboarding`. + + +```text +██████████████████████░░░░░░░░░░░░░░░░░░ 55% +``` + + +> [!INFO]- **Outstanding Tasks** +> +> - [ ] Surface attribution metadata in CLI output +> - [ ] Ledger snapshot automation +> + +### v1.0.0 Progress + +**What “v1.0.0” means:** Production-grade stability with complete semantics, attribution, and CI observability; ready for wider release and partner integrations. See `docs/planning/Milestone_Synthesis.md#v100-mdash-production-grade`. + + +```text +█████████████████████████████████░░░░░░░ 82% +``` + + +> [!INFO]- **Outstanding Tasks** +> +> - [ ] Adopt OID‑first equality everywhere (strict, no SHA fallback when OIDs present) +> - [ ] CI trend baselines collection +> + +## Status Model + +- Planned → In Progress → MVP → Alpha → Beta → v1.0.0 +- Stage names are canonical; “Shipped” is implied by milestone tags ≥ the current release. + +## Feature Lifecycle & Workflow + +Use the ledger as the single system of record from ideation through release: + +1. **Capture (Planned)** — When a new idea lands (roadmap review, retro, support signal), add a row under the appropriate group with a short name, owning area, and initial KLoC estimate. Link to any discovery notes/spec drafts in the “Spec” column and file a task in the `## Tasklist + +![Task Dependency Map](images/task_dag.svg) + +### MVP + +#### Core + + +- [ ] (GM.MVP.T001) Finalize strict equality +> [!INFO]- Step-by-step Instructions +> - [ ] 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. +> [!NOTE]- Dependencies +> - (none) + + +- [ ] (GM.MVP.T002) Negative tests for ref utils +> [!INFO]- Step-by-step Instructions +> - [ ] Extend `test_ref_utils` to cover invalid prefixes, length overflows, and reserved refs. +> - [ ] Ensure errors propagate with meaningful `GM_ERR_*` codes. +> - [ ] Update `docs/architecture/Ref_Name_Validation.md` with new failure scenarios. +> - [ ] Mention failure scenarios in README/tutorial examples. +> [!NOTE]- Dependencies +> - **Hard:** [GM.MVP.T001](#gmmvpt001) + +#### CLI + + +- [ ] (GM.MVP.T003) Extend CLI safety tests +> [!INFO]- Step-by-step Instructions +> - [ ] Expand CLI safety guard tests to cover host override permutations. +> - [ ] Document the override matrix in `docs/operations/Environment_Variables.md`. +> - [ ] Highlight safe override usage in README/CLI help. +> - [ ] Verify behavior in Docker CI harness. +> [!NOTE]- Dependencies +> - (none) + + +- [ ] (GM.MVP.T004) Query subcommands & diagnostics +> [!INFO]- Step-by-step Instructions +> - [ ] Design CLI syntax and update command help output for new query modes. +> - [ ] Implement parsing and output formatting for diagnostic flags. +> - [ ] Add unit/integration tests for diagnostic output. +> - [ ] Update tutorial examples with new query flows. +> [!NOTE]- Dependencies +> - **Hard:** [GM.MVP.T003](#gmmvpt003) + +#### Docs + + +- [ ] (GM.MVP.T005) Extend docs templates +> [!INFO]- Step-by-step Instructions +> - [ ] Audit existing document templates for missing sections (status, TOC guidance, review notes). +> - [ ] Add missing sections and refresh template examples. +> - [ ] Update docs tooling lint to enforce new expectations. +> - [ ] Announce template changes in the contributor guide. +> [!NOTE]- Dependencies +> - (none) + + +- [ ] (GM.MVP.T006) Tune AGENTS monthly rollup +> [!INFO]- Step-by-step Instructions +> - [ ] Define the monthly rollup cadence for AGENTS summaries. +> - [ ] Provide a script or automation to generate rollups. +> - [ ] Update `AGENTS.md` with cadence instructions. +> - [ ] Verify the pre-commit hook enforces the new format. +> [!NOTE]- Dependencies +> - **Soft:** [GM.MVP.T005](#gmmvpt005) + +#### Observability + + +- [ ] (GM.MVP.T007) CLI summary & snapshots +> [!INFO]- Step-by-step Instructions +> - [ ] Implement CLI summary command reading ledger progress. +> - [ ] Automate a cron/workflow to publish weekly snapshots. +> - [ ] Document the snapshot cadence and expectations. +> - [ ] Add regression test verifying README/ledger sync. +> [!NOTE]- Dependencies +> - **Soft:** [GM.MVP.T005](#gmmvpt005) + +### Alpha + +#### Core + + +- [ ] (GM.ALPHA.T001) Journal ordering tests +> [!INFO]- Step-by-step Instructions +> - [ ] Write table-driven tests exercising journal iteration order across branches. +> - [ ] Cover both plain and attributed edges in CBOR fixtures. +> - [ ] Document ordering guarantees in journal reader docs. +> - [ ] Wire new tests into `meson.build` and CI. +> [!NOTE]- Dependencies +> - **Hard:** [GM.MVP.T001](#gmmvpt001) + + +- [ ] (GM.ALPHA.T002) Cache perf tuning & benchmarks +> [!INFO]- Step-by-step Instructions +> - [ ] Capture baseline rebuild/query timings across representative repos. +> - [ ] Profile shard fan-out hotspots and prototype optimizations. +> - [ ] Automate a Docker-safe benchmark harness for regressions. +> - [ ] Document findings in `docs/architecture/bitmap-cache-design.md`. +> [!NOTE]- Dependencies +> - **Hard:** [GM.ALPHA.T001](#gmalphat001) + + +- [ ] (GM.ALPHA.T003) Edge diff coverage +> [!INFO]- Step-by-step Instructions +> - [ ] Create fixture branches with divergent edge histories to stress diffs. +> - [ ] Add integration tests comparing fan-in/out across merge commits. +> - [ ] Ensure cache/journal diff helpers handle branch fan-out scenarios. +> - [ ] Document the harness in `docs/architecture/System_Architecture.md`. +> [!NOTE]- Dependencies +> - **Hard:** [GM.ALPHA.T002](#gmalphat002) + + +- [ ] (GM.ALPHA.T004) Sandbox transactions +> [!INFO]- Step-by-step Instructions +> - [ ] Implement `gm_txn_start/apply/abort` helpers manipulating pending refs. +> - [ ] Build CLI plumbing (`git mind txn ...`) with dry-run/apply flows. +> - [ ] Add local/server hook snippets to enforce provenance. +> - [ ] Ship documentation updates (RFC, roadmap, tutorial snippet). +> [!NOTE]- Dependencies +> - **Hard:** [GM.ALPHA.T003](#gmalphat003) +> - **Hard:** [GM.MVP.T002](#gmmvpt002) + +#### CLI + + +- [ ] (GM.ALPHA.T005) Post-commit AUGMENTS wiring +> [!INFO]- Step-by-step Instructions +> - [ ] Finish journal read integration and handle path truncation errors. +> - [ ] Add end-to-end test demonstrating AUGMENTS edges emitted via hooks. +> - [ ] Document error/fallback behavior in hook guides. +> - [ ] Verify containerized tests cover the new path. +> [!NOTE]- Dependencies +> - **Hard:** [GM.ALPHA.T004](#gmalphat004) + + +- [ ] (GM.ALPHA.T006) GraphQL auto-resolve flag +> [!INFO]- Step-by-step Instructions +> - [ ] Introduce a feature flag controlling auto-resolve behavior. +> - [ ] Document validation workflow with screenshots in Review Seeding docs. +> - [ ] Outline GH App roadmap clarifying future automation. +> - [ ] Add regression tests around the new flag default. +> [!NOTE]- Dependencies +> - **Hard:** [GM.MVP.T004](#gmmvpt004) + +#### Observability + + +- [ ] (GM.ALPHA.T007) Structured logging & sampling +> [!INFO]- Step-by-step Instructions +> - [ ] Design logging schema for CBOR debug tracing. +> - [ ] Implement sampling controls (environment/config toggles). +> - [ ] Update operations docs with usage examples. +> - [ ] Add tests ensuring logs redact sensitive content. +> [!NOTE]- Dependencies +> - **Hard:** [GM.ALPHA.T004](#gmalphat004) + +### Beta + +#### Core + + +- [ ] (GM.BETA.T001) Multi-author attribution rollups +> [!INFO]- Step-by-step Instructions +> - [ ] Extend attribution data structure to track multiple authors and time windows. +> - [ ] Update cache/journal serialization to carry new metadata. +> - [ ] Add CLI flags to display attribution columns. +> - [ ] Document workflow and examples in attribution docs. +> [!NOTE]- Dependencies +> - **Hard:** [GM.ALPHA.T004](#gmalphat004) +> - **Soft:** [GM.MVP.T002](#gmmvpt002) + +### v1.0.0 + +#### Core + + +- [ ] (GM.V1.T001) Strict equality enforcement +> [!INFO]- Step-by-step Instructions +> - [ ] Harmonize equality helpers across cache/journal/util modules. +> - [ ] Add regression tests covering strict equality edges-to-cache interactions. +> - [ ] Document long-term guarantees in architecture docs. +> - [ ] Ensure release notes highlight the enforcement. +> [!NOTE]- Dependencies +> - **Hard:** [GM.ALPHA.T001](#gmalphat001) +> - **Hard:** [GM.MVP.T001](#gmmvpt001) + + +- [ ] (GM.V1.T002) Safe-string sweep +> [!INFO]- Step-by-step Instructions +> - [ ] Grep for raw `strcpy`/`sprintf` usage outside approved wrappers. +> - [ ] Replace with `gm_snprintf`/`gm_strcpy_safe` equivalents. +> - [ ] Update safety documentation and examples. +> - [ ] Add unit coverage where new wrappers are introduced. +> [!NOTE]- Dependencies +> - **Hard:** [GM.MVP.T001](#gmmvpt001) + +#### CLI + + +- [ ] (GM.V1.T003) Worksheet heuristics +> [!INFO]- Step-by-step Instructions +> - [ ] Define heuristic rules (placeholder detection, decision coverage). +> - [ ] Implement optional flag enabling heuristics in gate tooling. +> - [ ] Document opt-in usage and backward compatibility. +> - [ ] Add tests covering heuristic outcomes. +> [!NOTE]- Dependencies +> - **Hard:** [GM.MVP.T005](#gmmvpt005) +> - **Soft:** [GM.MVP.T006](#gmmvpt006) + +#### Observability + + +- [ ] (GM.V1.T004) CI trend baselines +> [!INFO]- Step-by-step Instructions +> - [ ] Instrument CI to capture tidy counts and unit failures. +> - [ ] Store trend data (JSON/CSV) for dashboards. +> - [ ] Render trend visualization in docs/dashboard. +> - [ ] Document maintenance workflow for trend data. +> [!NOTE]- Dependencies +> - **Hard:** [GM.ALPHA.T007](#gmalphat007) +> - **Soft:** [GM.MVP.T007](#gmmvpt007) + +## Completeness + +| Task | Started? | Finished? | Tested? | Shipped? | +|------|----------|-----------|---------|----------| +| [GM.MVP.T001](#gmmvpt001) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.MVP.T002](#gmmvpt002) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.MVP.T003](#gmmvpt003) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.MVP.T004](#gmmvpt004) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.MVP.T005](#gmmvpt005) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.MVP.T006](#gmmvpt006) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.MVP.T007](#gmmvpt007) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.ALPHA.T001](#gmalphat001) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.ALPHA.T002](#gmalphat002) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.ALPHA.T003](#gmalphat003) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.ALPHA.T004](#gmalphat004) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.ALPHA.T005](#gmalphat005) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.ALPHA.T006](#gmalphat006) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.ALPHA.T007](#gmalphat007) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.BETA.T001](#gmbetat001) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.V1.T001](#gmv1t001) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.V1.T002](#gmv1t002) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.V1.T003](#gmv1t003) | 🚫 | 🚫 | 🚫 | 🚫 | +| [GM.V1.T004](#gmv1t004) | 🚫 | 🚫 | 🚫 | 🚫 | diff --git a/docs/features/images/task_dag.dot b/docs/features/images/task_dag.dot new file mode 100644 index 00000000..23a94aa5 --- /dev/null +++ b/docs/features/images/task_dag.dot @@ -0,0 +1,124 @@ +digraph G { + rankdir=LR; + graph [pad="0.5", nodesep="0.6", ranksep="1.0"]; + node [shape=box, style=filled, fontname="IBM Plex Sans", fontsize=10]; + subgraph cluster_MVP { + label="Milestone: MVP"; + color="#666666"; + subgraph cluster_MVP_Core { + label="Core"; + color="#aaaaaa"; + "GM.MVP.T001" [label="GM.MVP.T001 +Finalize strict equality", fillcolor="#ffe599"]; + "GM.MVP.T002" [label="GM.MVP.T002 +Negative tests for ref utils", fillcolor="#ffe599"]; + } + subgraph cluster_MVP_CLI { + label="CLI"; + color="#aaaaaa"; + "GM.MVP.T003" [label="GM.MVP.T003 +Extend CLI safety tests", fillcolor="#cfe2f3"]; + "GM.MVP.T004" [label="GM.MVP.T004 +Query subcommands & diagnostics", fillcolor="#cfe2f3"]; + } + subgraph cluster_MVP_Docs { + label="Docs"; + color="#aaaaaa"; + "GM.MVP.T005" [label="GM.MVP.T005 +Extend docs templates", fillcolor="#ead1dc"]; + "GM.MVP.T006" [label="GM.MVP.T006 +Tune AGENTS rollup", fillcolor="#ead1dc"]; + } + subgraph cluster_MVP_Observability { + label="Observability"; + color="#aaaaaa"; + "GM.MVP.T007" [label="GM.MVP.T007 +CLI summary & snapshots", fillcolor="#d9ead3"]; + } + } + subgraph cluster_Alpha { + label="Milestone: Alpha"; + color="#666666"; + subgraph cluster_Alpha_Core { + label="Core"; + color="#aaaaaa"; + "GM.ALPHA.T001" [label="GM.ALPHA.T001 +Journal ordering tests", fillcolor="#ffe599"]; + "GM.ALPHA.T002" [label="GM.ALPHA.T002 +Cache perf tuning", fillcolor="#ffe599"]; + "GM.ALPHA.T003" [label="GM.ALPHA.T003 +Edge diff coverage", fillcolor="#ffe599"]; + "GM.ALPHA.T004" [label="GM.ALPHA.T004 +Sandbox transactions", fillcolor="#ffe599"]; + } + subgraph cluster_Alpha_CLI { + label="CLI"; + color="#aaaaaa"; + "GM.ALPHA.T005" [label="GM.ALPHA.T005 +Post-commit AUGMENTS wiring", fillcolor="#cfe2f3"]; + "GM.ALPHA.T006" [label="GM.ALPHA.T006 +GraphQL auto-resolve flag", fillcolor="#cfe2f3"]; + } + subgraph cluster_Alpha_Observability { + label="Observability"; + color="#aaaaaa"; + "GM.ALPHA.T007" [label="GM.ALPHA.T007 +Structured logging & sampling", fillcolor="#d9ead3"]; + } + } + subgraph cluster_Beta { + label="Milestone: Beta"; + color="#666666"; + subgraph cluster_Beta_Core { + label="Core"; + color="#aaaaaa"; + "GM.BETA.T001" [label="GM.BETA.T001 +Multi-author attribution rollups", fillcolor="#ffe599"]; + } + } + subgraph cluster_v1_0_0 { + label="Milestone: v1.0.0"; + color="#666666"; + subgraph cluster_v1_0_0_Core { + label="Core"; + color="#aaaaaa"; + "GM.V1.T001" [label="GM.V1.T001 +Strict equality enforcement", fillcolor="#ffe599"]; + "GM.V1.T002" [label="GM.V1.T002 +Safe-string sweep", fillcolor="#ffe599"]; + } + subgraph cluster_v1_0_0_CLI { + label="CLI"; + color="#aaaaaa"; + "GM.V1.T003" [label="GM.V1.T003 +Worksheet heuristics", fillcolor="#cfe2f3"]; + } + subgraph cluster_v1_0_0_Observability { + label="Observability"; + color="#aaaaaa"; + "GM.V1.T004" [label="GM.V1.T004 +CI trend baselines", fillcolor="#d9ead3"]; + } + } + "GM.MVP.T001" -> "GM.MVP.T002"; + "GM.MVP.T003" -> "GM.MVP.T004"; + "GM.MVP.T005" -> "GM.MVP.T006"; + "GM.MVP.T005" -> "GM.MVP.T007"; + "GM.MVP.T001" -> "GM.ALPHA.T001"; + "GM.ALPHA.T001" -> "GM.ALPHA.T002"; + "GM.ALPHA.T002" -> "GM.ALPHA.T003"; + "GM.ALPHA.T003" -> "GM.ALPHA.T004"; + "GM.MVP.T002" -> "GM.ALPHA.T004"; + "GM.ALPHA.T004" -> "GM.ALPHA.T005"; + "GM.MVP.T004" -> "GM.ALPHA.T006"; + "GM.ALPHA.T004" -> "GM.ALPHA.T007"; + "GM.ALPHA.T004" -> "GM.BETA.T001"; + "GM.MVP.T002" -> "GM.BETA.T001"; + "GM.ALPHA.T001" -> "GM.V1.T001"; + "GM.MVP.T001" -> "GM.V1.T001"; + "GM.MVP.T001" -> "GM.V1.T002"; + "GM.MVP.T005" -> "GM.V1.T003"; + "GM.MVP.T006" -> "GM.V1.T003"; + "GM.ALPHA.T007" -> "GM.V1.T004"; + "GM.MVP.T007" -> "GM.V1.T004"; +} \ No newline at end of file diff --git a/docs/features/images/task_dag.svg b/docs/features/images/task_dag.svg new file mode 100644 index 00000000..c0985e52 --- /dev/null +++ b/docs/features/images/task_dag.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:976b572cc44264c386c3c0b9b809787b611853908f57b42e6b3931fbf4fb80f3 +size 7963 diff --git a/src/attribution/attribution.md b/docs/legacy/src/attribution.md similarity index 91% rename from src/attribution/attribution.md rename to docs/legacy/src/attribution.md index 754bb35b..d7016a8f 100644 --- a/src/attribution/attribution.md +++ b/docs/legacy/src/attribution.md @@ -1,3 +1,13 @@ +--- +title: Legacy Notes — Attribution System +description: Archived implementation notes predating the core/ migration. +audience: [contributors] +domain: [legacy] +tags: [attribution, legacy] +status: archive +last_updated: 2025-09-17 +--- + # Attribution System The attribution system tracks who (human or AI) created each semantic edge, enabling collaborative knowledge building between humans and AI systems. diff --git a/src/edge/edge.md b/docs/legacy/src/edge.md similarity index 95% rename from src/edge/edge.md rename to docs/legacy/src/edge.md index 9dab35dd..2ac07f27 100644 --- a/src/edge/edge.md +++ b/docs/legacy/src/edge.md @@ -1,3 +1,13 @@ +--- +title: Legacy Notes — Edge Management +description: Historical design notes from the pre-core era covering edge structures and rationale. +audience: [contributors] +domain: [legacy] +tags: [edges, legacy] +status: archive +last_updated: 2025-09-17 +--- + # Edge Management ## Purpose diff --git a/src/util/sha.md b/docs/legacy/src/sha.md similarity index 95% rename from src/util/sha.md rename to docs/legacy/src/sha.md index 2125ddcb..25551f61 100644 --- a/src/util/sha.md +++ b/docs/legacy/src/sha.md @@ -1,3 +1,13 @@ +--- +title: Legacy Notes — SHA Resolution Utilities +description: Archived notes on SHA helpers prior to the core/ migration. +audience: [contributors] +domain: [legacy] +tags: [hashing, legacy] +status: archive +last_updated: 2025-09-17 +--- + # SHA Resolution Utilities ## Purpose diff --git a/docs/operations/CBOR_Debugging.md b/docs/operations/CBOR_Debugging.md new file mode 100644 index 00000000..4a28cefe --- /dev/null +++ b/docs/operations/CBOR_Debugging.md @@ -0,0 +1,31 @@ +--- +title: CBOR Debugging +description: How to enable CBOR decode tracing when reading journal commits. +audience: [developers] +domain: [operations] +tags: [cbor, debugging] +status: stable +last_updated: 2025-09-15 +--- + +# CBOR Debugging + +Enable verbose CBOR decode logging in the journal reader to troubleshoot malformed entries or golden vector issues. + +## Enable + +```bash +export GITMIND_CBOR_DEBUG=1 +``` + +The reader logs to `stderr`: + +- Offsets and remaining length when a decode fails +- Bytes consumed per decoded edge + +## Notes + +- Only affects CBOR decode paths in `core/src/journal/reader.c`. +- Use with Docker/CI only; don’t run core binaries against real repos on host. +- Disable by unsetting or setting to `0` / `false` / `no`. + diff --git a/docs/operations/Environment_Variables.md b/docs/operations/Environment_Variables.md index 4672b42f..789f4283 100644 --- a/docs/operations/Environment_Variables.md +++ b/docs/operations/Environment_Variables.md @@ -1,3 +1,13 @@ +--- +title: Environment Variables +description: Environment variables affecting build, CI, runtime, and tests. +audience: [developers] +domain: [operations] +tags: [environment, ci, runtime, cli] +status: stable +last_updated: 2025-09-15 +--- + # Environment Variables This page lists environment variables that influence git‑mind builds, tests, and runtime behavior. @@ -19,6 +29,11 @@ This page lists environment variables that influence git‑mind builds, tests, a - Used by: `meson.build` (host build e‑brake). - Values: `1` to allow; otherwise refused. +- `GITMIND_CI_IMAGE` + - Purpose: Select CI Docker image for local/CI runs that execute builds/tests in a container. + - Used by: `.github/workflows/c_core.yml`, `tools/ci/ci_local.sh`. + - Default: `gitmind/ci:clang-20`. + ## Runtime (CLI) - `GITMIND_SAFETY` @@ -27,6 +42,12 @@ This page lists environment variables that influence git‑mind builds, tests, a - Values: `off`, `0`, or `false` to disable the guard. - Notes: The guard uses libgit2 to inspect Git remotes and blocks only if a remote URL strictly matches the official repo path (`neuroglyph/git-mind[.git]`). +- `GITMIND_CBOR_DEBUG` + - Purpose: Enable verbose CBOR decode tracing in the journal reader. + - Used by: `core/src/journal/reader.c`. + - Values: `1`, `true`, or `yes` (case-insensitive) to enable; anything else disables. + - Notes: Prints offsets and decode attempts to `stderr`; use only for debugging. + - `GIT_MIND_SOURCE` - Purpose: Set attribution source for edges (e.g., `human`, `claude`, `gpt`, `system`, `import`). - Used by: Core attribution (`gm_attribution_from_env`). @@ -58,6 +79,19 @@ This page lists environment variables that influence git‑mind builds, tests, a - Used by: `tests/e2e/run_all_tests.sh` and related scripts. - Example: `export GIT_MIND=$(pwd)/build/git-mind` +- `GITMIND_TEST_BRANCH` + - Purpose: Direct journal writes to a specific branch in tests when the repository has no HEAD yet. + - Used by: `core/src/journal/writer.c` (test override). + - Example: `export GITMIND_TEST_BRANCH=testq` + - Notes: For tests only; production code should rely on current branch. + +## Git Hooks + +- `HOOKS_BYPASS` + - Purpose: Bypass local git hooks (e.g., the pre‑push CHANGELOG guard) in emergencies. + - Usage: `HOOKS_BYPASS=1 git push` + - Notes: Do not make a habit of bypassing hooks; update the CHANGELOG instead. + ## Notes - Prefer running builds and tests in Docker using the provided scripts. Host builds are intentionally gated to reduce accidental repository corruption. diff --git a/docs/planning/Milestone_Synthesis.md b/docs/planning/Milestone_Synthesis.md new file mode 100644 index 00000000..969a6c16 --- /dev/null +++ b/docs/planning/Milestone_Synthesis.md @@ -0,0 +1,84 @@ +--- +title: Milestone Synthesis & Scope Map +description: Consolidated view of MVP, Alpha, Beta, and v1.0.0 goals with cross-references to existing plans. +audience: [contributors] +domain: [planning] +tags: [milestones, roadmap] +status: draft +last_updated: 2025-09-16 +--- + +# Milestone Synthesis & Scope Map + +This guide aligns the many planning artifacts (HN demo plan, roadmap snapshots, milestone lists, release plans, brainstorms) into a single, milestone-oriented view. Use it when updating the [Features Ledger](../features/Features_Ledger.md) or when onboarding new contributors. + +## Table of Contents + +- [Quick Reference Table](#quick-reference-table) +- [Milestone Details](#milestone-details) + - [MVP — “Show HN” Ready](#mvp--show-hn-ready) + - [Alpha — Team Productivity](#alpha--team-productivity) + - [Beta — Community Onboarding](#beta--community-onboarding) + - [v1.0.0 — Production-Grade](#v100--production-grade) +- [How to Use This Doc](#how-to-use-this-doc) + +## Quick Reference Table + +| Milestone | Focus | Key Deliverables | Primary Sources | +|-----------|-------|------------------|-----------------| +| MVP | Prove the Git-native “mental model time-travel” loop; demo-ready flows. | Journal + AUGMENTS edges, core CLI (`link/list/cache`), deterministic semantics IDs, baseline cache rebuild + query, docs for demo. | `.legacy/HN Demo Plan.md`, `docs/planning/Milestones.md` (M0–M4), `docs/planning/Release_Plans.md` (v0.4–v0.6). +| Alpha | Team-ready tooling with reliable rebuilds and collaboration helpers. | Cohesion report basics, worksheet/review automation, cache perf guardrails, observability hooks, expanded CLI diagnostics. | `docs/planning/Milestones.md` (M5), `docs/planning/Release_Plans.md` (v0.7–v0.8), `docs/roadmap.md` (near/medium term). +| Beta | External contributor onboarding, advanced automation, packaging polish. | Advice merge + plugin hook stubs, CI hardening, packaging pipeline, attribution surfaced in CLI, telemetry snapshots. | `docs/planning/Milestones.md` (M6–M9), `docs/planning/Release_Plans.md` (v0.9–v0.10), `docs/wish-list-features/brain-storms/plugins.md`. +| v1.0.0 | Production-grade stability with complete semantics + attribution story. | OID-first equality everywhere, CI trend baselines, packaging/release hygiene, documentation freeze, optional MCP service (if landed). | `docs/planning/Release_Plans.md` (v0.10 + v0.11 optional), `docs/planning/Product_Roadmap.md`, `docs/planning/Milestones.md`. + +## Milestone Details + +### MVP — “Show HN” Ready + +- **Experience goal:** Deliver the 30-second hook from `.legacy/HN Demo Plan.md` — initialize, link, time-travel, list, and traverse understanding history. +- **Scope highlights:** + - Journal append + AUGMENTS hook (Milestones M3), deterministic semantics IDs (M1), CBOR names (M2). + - CLI basics (`link`, `list`, `cache-rebuild`) with helpful errors (Release v0.4–v0.6). + - Baseline cache to support `git mind list` across history (Product Roadmap Themes A/B/C). + - Docs + scripts to reproduce the demo. +- **Exit criteria:** A maintainer can run the HN demo on a fresh repo, share reproducible docs, and recover after cache/journal reset. + +### Alpha — Team Productivity + +- **Experience goal:** Internal team uses git-mind daily with confidence in rebuilds and collaboration flows. +- **Scope highlights:** + - Cohesion report + branch-aware queries (Milestones M5, Release v0.7–v0.8). + - Worksheet automation + reply workflows (Features Ledger “Review Seeding & Replies”). + - Cache performance guardrails (<10ms median per Release Plan), telemetry knobs (Roadmap “Medium Term”). + - Sandbox transaction blocks to preview journal mutations safely before apply (`docs/talk-shop/Sandbox_Transactions.md`). + - Observability: CBOR debug tracing, ledger snapshot tasks. +- **Exit criteria:** Cache rebuilds complete without manual babysitting; collaboration loops (worksheet, review replies) exercised on real PR; alpha docs outline known gaps. + +### Beta — Community Onboarding + +- **Experience goal:** External contributors can install, review, and extend git-mind without maintainer hand-holding. +- **Scope highlights:** + - Advice merge CRDT + plugin hook scaffolding (Milestones M6–M9, Release v0.9–v0.10). + - Attribution surfaced in CLI output; ledger snapshots automated for transparency. + - Packaging + CI hardening; tidy-zero guard for core modules. + - Plugin vision seeds (wishlist brainstorm) inform optional experiments. +- **Exit criteria:** Nightly builds + packaging succeed, documentation covers install/update flows, advanced review automation stable, plugin hooks gated but demonstrable. + +### v1.0.0 — Production-Grade + +- **Experience goal:** git-mind installs cleanly, passes strict CI, and offers a complete semantics + attribution story suitable for partners. +- **Scope highlights:** + - OID-first equality everywhere; safe wrappers swept (ledger tasks, Release Plan). + - CI trend baselines & observability dashboards. + - Packaging artifacts with checksums + version policy (Release v0.10) plus optional MCP service (v0.11) if ready. + - Documentation freeze + API stability commitments (Roadmap long-term). +- **Exit criteria:** All v0.x acceptance criteria met, outstanding ledger tasks closed or re-scoped, README/Features Ledger alignment signed off. + +## How to Use This Doc + +1. **Feature Ledger updates:** Pick the milestone whose exit criteria match the feature’s ambition; update the `Milestone` column accordingly. +2. **Task tagging:** Prefix checklist items with `[MVP]`, `[Alpha]`, `[Beta]`, or `[v1.0.0]` to feed the ledger callouts. +3. **Planning syncs:** Reference the Quick Reference table during roadmap discussions to ensure new ideas map to an existing milestone (or justify a new one). +4. **Docs hygiene:** When adding new planning material, include a “Milestone Alignment” note linking back here to keep the taxonomy coherent. + +Keeping these milestones aligned across docs prevents drift and keeps our progress bars meaningful. diff --git a/docs/planning/Milestones.md b/docs/planning/Milestones.md index 6e421ce6..b1d24e58 100644 --- a/docs/planning/Milestones.md +++ b/docs/planning/Milestones.md @@ -1,3 +1,13 @@ +--- +title: Milestones +description: Planning artifacts: roadmap, releases, sprints, milestones. +audience: [contributors] +domain: [project] +tags: [planning] +status: archive +last_updated: 2025-09-15 +--- + # Milestones Table of Contents diff --git a/docs/planning/Product_Roadmap.md b/docs/planning/Product_Roadmap.md index 99cbac89..177b6d55 100644 --- a/docs/planning/Product_Roadmap.md +++ b/docs/planning/Product_Roadmap.md @@ -1,3 +1,13 @@ +--- +title: Product Roadmap +description: Planning artifacts: roadmap, releases, sprints, milestones. +audience: [contributors] +domain: [project] +tags: [planning] +status: archive +last_updated: 2025-09-15 +--- + # Product Roadmap Table of Contents diff --git a/docs/planning/Release_Plans.md b/docs/planning/Release_Plans.md index f192efb1..7e3e666c 100644 --- a/docs/planning/Release_Plans.md +++ b/docs/planning/Release_Plans.md @@ -1,3 +1,13 @@ +--- +title: Release Plans +description: Planning artifacts: roadmap, releases, sprints, milestones. +audience: [contributors] +domain: [project] +tags: [planning] +status: archive +last_updated: 2025-09-15 +--- + # Release Plans Table of Contents diff --git a/docs/planning/Sprint_Plans.md b/docs/planning/Sprint_Plans.md index a10f0479..d2a712c6 100644 --- a/docs/planning/Sprint_Plans.md +++ b/docs/planning/Sprint_Plans.md @@ -1,3 +1,13 @@ +--- +title: Sprint Plans +description: Planning artifacts: roadmap, releases, sprints, milestones. +audience: [contributors] +domain: [project] +tags: [planning] +status: archive +last_updated: 2025-09-15 +--- + # Sprint Plans Table of Contents diff --git a/docs/quality/Markdown_Style_Guide.md b/docs/quality/Markdown_Style_Guide.md index 1d9cdd4e..baa3d195 100644 --- a/docs/quality/Markdown_Style_Guide.md +++ b/docs/quality/Markdown_Style_Guide.md @@ -1,3 +1,13 @@ +--- +title: Markdown Style Guide +description: Project-wide rules for formatting Markdown and using markdownlint. +audience: [contributors] +domain: [quality] +tags: [markdown, lint] +status: stable +last_updated: 2025-09-15 +--- + # Markdown Style Guide Table of Contents diff --git a/docs/quality/SOLID_DRY_REPORT.md b/docs/quality/SOLID_DRY_REPORT.md index aa368acb..e889ecf5 100644 --- a/docs/quality/SOLID_DRY_REPORT.md +++ b/docs/quality/SOLID_DRY_REPORT.md @@ -1,3 +1,13 @@ +--- +title: 🚨 SOLID/DRY Compliance Report - CRITICAL SECURITY ASSESSMENT +description: Assessment of the codebase against SOLID and DRY with remediation guidance. +audience: [contributors, developers] +domain: [quality] +tags: [solid, dry] +status: draft +last_updated: 2025-09-15 +--- + # 🚨 SOLID/DRY Compliance Report - CRITICAL SECURITY ASSESSMENT Table of Contents diff --git a/docs/quality/TIDY_CLANG_CRUSADE.md b/docs/quality/TIDY_CLANG_CRUSADE.md index fe7299c7..cf751060 100644 --- a/docs/quality/TIDY_CLANG_CRUSADE.md +++ b/docs/quality/TIDY_CLANG_CRUSADE.md @@ -1,3 +1,13 @@ +--- +title: 🚨 The Great Clang-Tidy Crusade +description: Tracking clang-tidy issues and progress toward zero warnings. +audience: [contributors, developers] +domain: [quality] +tags: [clang-tidy] +status: draft +last_updated: 2025-09-15 +--- + # 🚨 The Great Clang-Tidy Crusade Table of Contents diff --git a/docs/quality/local-ci-parity.md b/docs/quality/local-ci-parity.md index 80c7f0c6..4706bf9b 100644 --- a/docs/quality/local-ci-parity.md +++ b/docs/quality/local-ci-parity.md @@ -1,3 +1,13 @@ +--- +title: Local CI Parity (Docker) +description: How to run CI-equivalent builds and lint locally using project Docker images. +audience: [contributors, developers] +domain: [quality] +tags: [docker, ci, clang-tidy] +status: stable +last_updated: 2025-09-15 +--- + # Local CI Parity (Docker) Table of Contents diff --git a/docs/quality/static-analysis-guide.md b/docs/quality/static-analysis-guide.md index 180173bd..07e8664d 100644 --- a/docs/quality/static-analysis-guide.md +++ b/docs/quality/static-analysis-guide.md @@ -1,3 +1,13 @@ +--- +title: Static Analysis Tools for Git-Mind +description: Overview of clang-tidy and related tools for static analysis in this repo. +audience: [contributors, developers] +domain: [quality] +tags: [clang-tidy, static-analysis] +status: draft +last_updated: 2025-09-15 +--- + # Static Analysis Tools for Git-Mind Table of Contents @@ -134,7 +144,7 @@ __Strengths__: Interprocedural, incremental analysis pip install flawfinder # Run -flawfinder --html --context src/ > security-report.html +flawfinder --html --context core/src/ > security-report.html ``` __Catches__: Security vulnerabilities, unsafe functions diff --git a/docs/quality/tools-vs-custom-checks.md b/docs/quality/tools-vs-custom-checks.md index c07f74e6..df073219 100644 --- a/docs/quality/tools-vs-custom-checks.md +++ b/docs/quality/tools-vs-custom-checks.md @@ -1,3 +1,13 @@ +--- +title: Standard Tools vs Custom Checks +description: Replace bespoke linters with robust, configurable standard tools. +audience: [contributors, developers] +domain: [quality] +tags: [lint, tooling] +status: stable +last_updated: 2025-09-15 +--- + # Standard Tools vs Custom Checks Table of Contents diff --git a/docs/requirements/Requirements.md b/docs/requirements/Requirements.md index fd465bb8..c56f1605 100644 --- a/docs/requirements/Requirements.md +++ b/docs/requirements/Requirements.md @@ -1,3 +1,13 @@ +--- +title: Requirements +description: Functional and non-functional requirements for git-mind. +audience: [contributors, developers] +domain: [project] +tags: [requirements] +status: draft +last_updated: 2025-09-15 +--- + # Requirements Table of Contents diff --git a/docs/risk/Risk_Register.md b/docs/risk/Risk_Register.md index 2df543d1..eda95590 100644 --- a/docs/risk/Risk_Register.md +++ b/docs/risk/Risk_Register.md @@ -1,3 +1,13 @@ +--- +title: Risk Register +description: Top risks, mitigations, and contingencies for git-mind. +audience: [contributors, developers] +domain: [project] +tags: [risk] +status: draft +last_updated: 2025-09-15 +--- + # Risk Register Table of Contents diff --git a/docs/sitrep/2025-06-24/ci-sitrep.md b/docs/sitrep/2025-06-24/ci-sitrep.md index c96714a4..85d28eeb 100644 --- a/docs/sitrep/2025-06-24/ci-sitrep.md +++ b/docs/sitrep/2025-06-24/ci-sitrep.md @@ -1,3 +1,13 @@ +--- +title: CI Infrastructure SITREP - 2025-06-24 +description: Situation reports and CI notes. +audience: [contributors] +domain: [quality] +tags: [sitrep] +status: archive +last_updated: 2025-09-15 +--- + # CI Infrastructure SITREP - 2025-06-24 ## Executive Summary diff --git a/docs/specs/Technical_Specifications.md b/docs/specs/Technical_Specifications.md index 0d086bea..d96f55df 100644 --- a/docs/specs/Technical_Specifications.md +++ b/docs/specs/Technical_Specifications.md @@ -1,6 +1,16 @@ +--- +title: Technical Specifications +description: Detailed specifications for semantics, cache integration, and advice. +audience: [developers] +domain: [architecture] +tags: [specs] +status: draft +last_updated: 2025-09-15 +--- + # Technical Specifications -Table of Contents +## Table of Contents - [Executive Summary](#executive-summary) - [Implementation Approach](#implementation-approach) diff --git a/docs/talk-shop/Graph-Lvl-Up-Tank.md b/docs/talk-shop/Graph-Lvl-Up-Tank.md new file mode 100644 index 00000000..369bbcd3 --- /dev/null +++ b/docs/talk-shop/Graph-Lvl-Up-Tank.md @@ -0,0 +1,93 @@ +--- +title: Graph LvL-Up Tank +description: Crash-course to power-level new contributors into graph-native thinking for git-mind. +audience: [contributors] +domain: [learning] +tags: [graphs, onboarding, best-practices] +status: draft +last_updated: 2025-09-16 +--- + +# Graph LvL-Up Tank 🛡️🧠 + +Welcome to the tank queue. Strap in; by the time you exit, you should be dangerous enough to model git-mind semantics without summoning undefined behavior—or a disappointed reviewer. + +## 0. Mindset Buffs (Pre-pull Ritual) +- **Everything is an edge first, artifact second.** Start by naming the relationship (`implements`, `touches`, `reflects`), then pick endpoints. If you can’t articulate the edge, you’re not ready to store the nodes. +- **Paths are narratives.** A commit-to-retro chain is a story. Store the raw events, then decide which shortcuts (journey nodes, convenience edges) the UX needs. +- **Filters > Firehoses.** Plan for edge-type and node-type filters in every traversal. Hidden subgraphs are the secret sauce; surface them deliberately. +- **Rebuild is cheap, provenance isn’t.** Derived edges are disposable. Canonical edges (journal entries) must be traceable and time-travel safe. + +## 1. Core Knowledge Slots (Allocate 5 Skill Points) +1. **Graph Math Basics** + - Directed acyclic vs. cyclic; strongly connected components. + - Path length (degree/separation) and why we cap N in CLI output. +2. **Query Languages** + - Cypher pattern: `MATCH (a:Issue)-[:RESOLVED_IN]->(f:File)` → mental template for git-mind queries. + - Gremlin grok: `g.V().hasLabel('file').out('implements')` — stream mindset for CLI pipelines. +3. **Indexing** + - Node key indexes (file path + OID), composite indexes for `(:Edge {type, lane})`. + - Materialized projections (journey nodes, globs) to accelerate user-facing flows. +4. **Temporal Semantics** + - Effective-from / effective-to windows, commit ULIDs, cache snapshots. + - How time-aware queries keep “what was true at C2?” deterministic. +5. **Conflict Handling** + - Merge strategies: union vs. CRDT for advice edges, last-writer-wins metadata. + - Review worksheets as capture devices, not just documentation. + +## 2. git-mind Native Patterns +- **Journey Nodes (a.k.a. path bundles)** + - Use when the same path is replayed often (Issue → PR → Review → Merge → Retro). + - Store ordered edge IDs + timestamps; connect journey ⇄ participants for hop-1 access. +- **Convenience Edges** + - Materialize on workflows closing: `issue --touched--> file`, `retro --reflects--> file`. + - Regenerate on cache rebuild; mark `derived=true` for hygiene. +- **Edge Taxonomy** + - `primary`: canonical journal entry (AUGMENTS edge). + - `secondary`: manual curation (`implements`, `verified_by`). + - `advisory`: AI suggestions, dashed edges in the SVG; require verification state. +- **OID-first Everything** + - Node keys: `(type, repo_id, oid)`. + - Edge keys: `(src_oid, dst_oid, type, ulid)` to guarantee time-travel diffing. + +## 3. Query Recipes (Tank Macros) +| What you want | Pattern | Notes | +| --- | --- | --- | +| “All artifacts tied to this file” | `MATCH (f:File {oid:$oid})-[:TOUCHES|:REFLECTS|:JOURNEY_HAS*1..2]->(x) RETURN x` | Include journey bundles for 1-hop hits. | +| “Which commits closed issue #42?” | `MATCH (i:Issue {number:42})-[:RESOLVED_IN]->(j:Journey)-[:MERGED_IN]->(c:Commit) RETURN c` | Journey keeps metadata; fallback to merge edges. | +| “Retro notes within 2 hops of auth.c” | `MATCH (f:File {path:'core/src/auth.c'})-[:REFLECTS\|:JOURNEY_HAS*1..2]->(r:Retro) RETURN DISTINCT r` | Filters keep CLI output sane. | +| “What changed between C2 and C5?” | Use timeline diff: `gm timeline diff --from C2 --to C5 --edge-types implements,verified_by` | Rely on cache deltas; avoid brute-force BFS. | + +## 4. Modeling Checklist (Before You Commit Your Thoughts) +- [ ] Edge types named verb-first (`implements`, `reflects`, `verified_by`). +- [ ] Node labels single-responsibility (no `DocOrIssue` mashups). +- [ ] Every derived edge cites its source journal ULID. +- [ ] Path bundle recorded if more than three steps are replayed in UX. +- [ ] Filters documented: which queries surface this edge? Which layers hide it? + +## 5. Toolchain & Playground +- **Local Sandbox**: Use `cypher-shell` or `memgraph` for synthetic modeling before touching git refs. +- **Visualization**: Keep the animated SVG (assets/images/git-mind-dag.svg) as the mental map; update it when semantics evolve. +- **Performance Drills**: `EXPLAIN` queries (even conceptually) to spot where indexes matter. If a traversal requires >3 hops routinely, consider a projection. +- **Docs Backfill**: Update `docs/architecture/*` whenever you invent a new edge type or path strategy; ledger tasks depend on that clarity. + +## 6. Level-Up Quests (XP Farming) +1. Run `git mind list --from core/src/auth.c --with-attribution` and sketch the path you *expected* vs. what exists. Gap? Add edges or journeys. +2. Model Issue → Review → Merge as a journey node in a scratch graph; verify you can reach every participant in ≤2 hops. +3. Draft a Cypher query that respects time-travel (`WHERE c.ulid BETWEEN $from AND $to`). Translate it into git-mind CLI verbs. +4. Teach someone else the difference between canonical vs. derived edges. If you can’t explain it in one whiteboard, repeat this tank session. + +## 7. Raid Boss Tips +- **Graph drift shows up as surprise hops.** When a traversal suddenly needs N+2 steps, inspect recent merges; you probably missed a convenience edge. +- **Don’t promote AI advice to canonical without human sign-off.** Keep `verified_by` state explicit; blank verification is villain fuel. +- **Measure before refactoring.** Snapshot traversal counts/latency before “optimizing” edge types. Graph debt hides in assumptions. +- **Time-travel kills shortcuts.** If an edge isn’t reproducible at C1…Cn, it doesn’t belong in the journal. Derived edges can time-shift; canonical can’t. + +## 8. TL;DR Buff Sheet +- Model narratives, not just nodes. +- Use journey nodes + convenience edges to balance fidelity and ergonomics. +- Keep queries filter-rich and depth-limited. +- Treat OIDs and ULIDs as sacred; everything else is rebuildable. +- Document every new edge type the moment it spawns. + +Queue is clear. Mount awarded. Go forth and mind the graph. diff --git a/docs/talk-shop/Sandbox_Transactions.md b/docs/talk-shop/Sandbox_Transactions.md new file mode 100644 index 00000000..b5485f03 --- /dev/null +++ b/docs/talk-shop/Sandbox_Transactions.md @@ -0,0 +1,110 @@ +--- +title: Sandbox Transaction Blocks +description: RFC to stage git-mind graph mutations inside inspectable transaction branches before committing to the journal. +audience: [contributors] +domain: [architecture] +tags: [journal, workflow, sandbox] +status: draft +last_updated: 2025-09-17 +--- + +# Sandbox Transaction Blocks + +## Why + +As we migrate fully into `core/` and tighten our conformance tests, the next friction point is confidence in graph mutations. Contributors (humans, plugins, or AI helpers) often want to queue a series of edges, inspect the result, then decide whether to land it. Today the only options are: + +- append directly to `refs/gitmind/edges/` and hope CI catches bad edges later +- hand-roll temporary refs and clean them up manually +- bypass the hooks with `HOOKS_BYPASS=1` + +We can do better by giving every mutation a sandboxed transaction. + +## Goals + +- **Preview first** – let devs and tools see exactly what a batch of edges will do before touching the canonical refs. +- **Make journaling safe** – journal entries authored in a transaction never appear in the main DAG until applied. +- **Shareable drafts** – a transaction should be easy to hand off (txn ID/ref), so reviewers can inspect deltas without bespoke tooling. +- **Integrate with cache rebuilds** – transaction refs must be invisible to cache/journal consumers unless explicitly targeted. +- **Zero surprise for CI** – transactions that were never applied should not affect builds/tests. + +Non-goals for the first iteration: multi-user locking, conflict resolution UI, or automatic rebase of transactions during apply. + +## Proposed Design + +### Ref Layout + +``` +refs/gitmind/ + edges/ # canonical journal + pending///edges/ # sandbox transactions (namespaced) + pending///meta # json metadata for the txn + applied/ # optional archival pointer +``` + +- Transactions are ULID-keyed and namespaced by the author (CLI derives `` from git config or `GITMIND_TXN_NAMESPACE`). This prevents cross-claiming someone else’s sandbox. +- Metadata (JSON) records author, branch target, base journal OID, start time, edge-count hash, and applied-by provenance. Payload edges mirror the canonical layout so replay logic can reuse existing helpers. + +### CLI Flow + +1. `git mind txn start [--branch main]` → returns txn ID, records base journal OID. +2. Subsequent `git mind link/list/...` commands operate inside the transaction by default (reads merge pending + canonical, writes hit pending). +3. `git mind txn show [--format table|json|diff]` renders edges staged in the transaction. +4. Users choose `git mind txn apply [--branch main]` (alias: `txn commit`) or `git mind txn abort`. + +`git mind txn show --format diff` is the default preview; `--dry-run` performs the apply simulation without mutating refs (useful for CI or pre-review). + +### Apply Logic + +- Collect pending commits for ``. +- Confirm the canonical journal tip still equals the recorded base OID (fast-forward); else replay on top (append new commits) and surface schema/ULID conflicts loudly. +- Stamp the apply commit with trailers (`Gm-Txn:` and `Gm-Apply:`) for provenance. +- Update `refs/gitmind/edges/` and move the transaction ref under `refs/gitmind/applied/` (retention policy below) or prune it if configured. +- Trigger cache rebuild hooks or mark the cache dirty if the branch matches current context. + +### Abort Logic + +- Drop the pending ref; the CLI should refuse to abort if the transaction was already applied to avoid footguns. + +### Concurrency / Conflicts + +- If the canonical ref moved while the transaction was active, we replay the edges as fresh commits: read CBOR payloads, re-run append logic, and create new journal commits. +- If replay encounters conflicts (duplicate ULIDs, incompatible schema), we surface the errors before applying. + +### Hooks & Tests + +- Local hooks (pre-push) should block accidental pushes of `refs/gitmind/pending/*` unless an override flag is set; server-side hooks enforce trailers on canonical refs and optionally auto-archive transactions (see worked example in `Sandbox_Transactions_Feedback.md`). +- Unit tests for `gm_journal` should ensure reads can accept a transaction ref override (`gm_journal_read_txn(ctx, txn_id, ...)`). +- E2E: script that starts a transaction, stages edges, aborts, verifies canonical journal unchanged; then repeat with apply and validate. + +## Implementation Phases + +1. **Scaffold** – add `gm_txn_*` helpers (start/apply/abort) that manipulate Git refs only; no CLI surface yet. +2. **CLI Preview** – wire `git mind txn` commands, default writes to a transaction when `GITMIND_TXN_ID` is set (useful for `make ci-local` harnesses). +3. **Opt-in Auto-Txn** – add `--txn` flag, environment variable, and docs. CI remains “apply-only”; developers manually apply before running pipelines. +4. **Retention + Enforcement** – implement configurable retention (`git config gitmind.txn.retention=keep|days=|delete`) and disallow direct journal writes outside of a transaction unless `--apply` is passed explicitly (with a hook guard). + +Default retention proposal: + +- Pending refs are pruned automatically on abort. +- Applied refs are archived under `refs/gitmind/applied/` for 14 days by default, overridden via config or `git mind txn apply --no-archive`. +- A periodic `git mind txn gc` command prunes expired archives. + +## Decisions & Remaining Questions + +- ✅ **Retention** — archive applied transactions for 14 days by default with `gitmind.txn.retention` override; pending refs auto-pruned on abort/apply. +- ✅ **CI strategy** — CI remains “apply-only”; pipelines fail fast if pending refs are pushed. Developers must apply (or use dry-run apply locally) before running `make ci-local`. +- ✅ **Namespaces & metadata** — each transaction stores author, base OID, branch, start time, edge digest, and apply provenance; refs are namespaced by actor. +- ✅ **Replay semantics** — schema violations or duplicate ULIDs abort apply. No silent skipping; users fix the transaction then re-apply. +- ❓ **Review tooling** — explore integrating transaction IDs into worksheet/review seeding so bots can comment before apply. +- ❓ **Rebase support** — evaluate need for `git mind txn rebase` once transactions are in heavy use. + +## Related Work + +- `docs/features/Features_Ledger.md` (new tasks around cache/journal confidence) +- `docs/talk-shop/Graph-Lvl-Up-Tank.md` (journey node + transaction storytelling) +- Existing cache conformance tests and the recently added branch/tree coverage (`core/tests/unit/test_cache_branch_limits.c`, `test_cache_tree_size.c`) + +--- + +Feedback welcome—especially on the remaining review/rebase questions. A worked end-to-end example (including hook snippets) lives in `docs/talk-shop/Sandbox_Transactions_Feedback.md` for quick reference. diff --git a/docs/talk-shop/Sandbox_Transactions_Feedback.md b/docs/talk-shop/Sandbox_Transactions_Feedback.md new file mode 100644 index 00000000..53a4326a --- /dev/null +++ b/docs/talk-shop/Sandbox_Transactions_Feedback.md @@ -0,0 +1,699 @@ +# A structured RFC review of your Sandbox Transaction Blocks draft + +Here's what’s solid, what needs tightening, and a few extra sharp edges you might want to grind down before calling it “ready.” + +## Strengths + +• Clear motivation – You nailed the “why.” Current friction points are well-explained (manual refs, bypass flags, CI catching things too late). That grounds the proposal in contributor pain. +• Concrete goals – Each goal maps to a real workflow: preview, journaling safety, shareability, cache isolation, CI hygiene. Good scoping (no UI/rebase in v1). +• Ref layout sketch – Showing pending/ and applied/ alongside canonical is clean, Git-idiomatic, and composable. +• CLI flow – The numbered workflow (txn start → operate → show → apply/abort) reads intuitive. Good developer ergonomics. +• Apply/abort semantics – Covers fast-forward expectations, replay logic, and error surfacing. That’s the hardest part and you’ve already drawn the boundaries. +• Testing hooks – Explicit mention of both unit and E2E tests at this stage is solid discipline. + +## Weak Spots / Risks + +• Ref explosion – Every transaction spawns a ref subtree. Without a cleanup strategy, you’ll drown in pending/ and applied/ clutter. Right now you punt on retention. +• Rebase/replay cost – Replay edge CBOR payloads on schema drift is non-trivial. You call it “re-run append logic,” but that may need its own test suite (duplicate ULIDs vs. schema version bumps are very different beasts). +• Cache invisibility – You say “must be invisible unless explicitly targeted,” but don’t outline how consumers distinguish. A flag in gm_journal_read_txn is fine, but you’ll need guardrails so rogue readers don’t accidentally pull pending refs. +• Concurrency model – No multi-user locking is fine for v1, but you should acknowledge the risk of racey concurrent applies. Without at least a warning system, two contributors could both “fast-forward” in their own heads and then clobber. +• CI semantics – It’s fuzzy whether CI should (a) always ignore pending refs, (b) run tests only on applied, or (c) optionally simulate applying pending. Needs a decision, not just an open question. + +## Suggestions / Additions + +• Retention knobs – Decide early: +• Default = applied refs get GC’d after X days, + Opt-in = keep forever for audit, +• Configurable = git config gitmind.txn.retention=keep|prune|days. +• Namespace isolation – To address your own security open question, prefix refs/gitmind/pending//; avoids accidental cross-claiming of someone else’s txn. +• Transaction metadata – ULID is good, but include at least: author, start time, base OID, branch target, maybe hash of staged payloads. Makes audits and replay debugging saner. +• “Dry-run apply” mode – Before doing the irreversible, let the CLI show exactly what edges will be appended if applied now (like git rebase --show-current-patch). +• CLI ergonomics – Consider git mind txn commit (synonym for apply) so the git muscle memory flows naturally. +• Hooks – Explicitly note that pre-push/pre-commit should ignore pending/ by default, to stop confusion. + +## Open Questions (Reframed) + +1. Retention – Are applied transactions first-class audit artifacts or temporary scaffolding? Decide now, else you’ll regret dangling refs. +2. CI role – Do you want CI to simulate transactions (auto-apply + rollback), or force devs to apply first? My take: start with manual apply only, else CI semantics balloon. +3. Security – Is namespacing enough, or do you need RLS-like ACLs for multi-user repos? For single-user local use, probably overkill. +4. Replay semantics – Are schema errors fatal (abort apply) or recoverable (skip edge)? Define strictness before contributors start relying on undefined behavior. + +## Verdict + +Great draft. The bones are strong: you’ve got a git-native layout, intuitive CLI, and clear apply/abort semantics. The two dragons ahead are retention sprawl and CI ambiguity. Solve those, and v1 is both achievable and useful. + +## A Worked Example + +I wrote it in narrative CLI style so contributors can see the whole lifecycle without reading walls of prose. + +### Worked Example: Transaction Lifecycle + +#### 1. Start a transaction + +Create a sandbox to stage edges against main. + +```bash +$ git mind txn start --branch main +txn started: 01J7A3S5J0B9QHY1TG8RZVZPWF +base: 9f3c12a (refs/gitmind/edges/main) +``` + +This creates refs under: + +```bash +refs/gitmind/pending/01J7A3S5J0B9QHY1TG8RZVZPWF/ + meta + edges/main +``` + +#### 2. Stage edges inside the transaction + +All git mind commands now write to the pending ref (not canonical). + +```bash +$ git mind link A B --rel depends_on +$ git mind link B C --rel implements +``` + +The canonical journal (`edges/main`) is untouched. + +#### 3. Inspect staged changes + +Preview what would land if applied now. + +```bash +$ git mind txn show --format diff ++ edge A → B (depends_on) ++ edge B → C (implements) +``` + +You can also dump JSON for programmatic review: + +```bash +$ git mind txn show --format json > txn-review.json +``` + +### 4. Abort (optional) + +If you decide the batch is junk: + +```bash +$ git mind txn abort 01J7A3S5J0B9QHY1TG8RZVZPWF +txn aborted; pending refs removed +``` + +The canonical journal remains unchanged. + +### 5. Apply (happy path) + +If the batch looks good: + +```bash +$ git mind txn apply 01J7A3S5J0B9QHY1TG8RZVZPWF +applied to refs/gitmind/edges/main at 9f3c12a → 5c7d9be +archived under refs/gitmind/applied/01J7A3S5J0B9QHY1TG8RZVZPWF +``` + +Now `edges/main` has the two new commits, and the transaction is archived. + +### 6. What if main moved? + +If someone else updated the journal in the meantime: + +```bash +$ git mind txn apply 01J7A3S5J0B9QHY1TG8RZVZPWF +base mismatch (expected 9f3c12a, found e4d2b71) +→ replaying edges… +→ appended new commits cleanly +applied at e4d2b71 → 8a9f11c +``` + +The tool transparently replays CBOR payloads on top of the new tip. +Conflicts (e.g. duplicate ULIDs) surface before finalizing. + +That’s the whole dance: start → stage → show → abort/apply → archive. + +## A mock meta file + +To show contributors what lives under `refs/gitmind/pending//meta`. I kept it in CBOR-friendly JSON, since that’s what your edges already use. + +### Example: Transaction Metadata + +```json +{ + "txn_id": "01J7A3S5J0B9QHY1TG8RZVZPWF", // ULID for uniqueness + sort order + "author": { + "name": "J. Kirby Ross", + "email": "james@flyingrobots.dev" + }, + "branch": "main", // target journal branch + "base_oid": "9f3c12a", // OID of journal head at txn start + "created_at": "2025-09-17T20:41:12Z", // ISO8601 timestamp + "applied_at": null, // set once txn applied + "status": "pending", // pending | applied | aborted + "edge_count": 2, // count of staged edges + "hash": "sha256-0f9f1c2...", // hash of all staged edge payloads + "notes": "Initial draft links A→B, B→C" // optional freeform field +} +``` + +### Notes + +• txn_id – ULID makes it sortable and globally unique. +• base_oid – allows fast-forward check before apply. +• hash – gives you immutability; lets CI verify payload integrity. +• status – a single field for lifecycle transitions. +• applied_at – stays null until applied; doubles as audit trail if you archive. +• notes – cheap way to let humans drop a comment. + +## Sketch of a minimal schema/validation ruleset + +Think: like a JSON Schema or CBOR schema, so contributors can’t bikeshed later about what’s “required” vs. “optional”? That way you bake enforcement right into phase 1. + +A tight, opinionated JSON Schema + ruleset for `refs/gitmind/pending//meta`. Drop this straight into the RFC. + +### Transaction Metadata Schema & Validation + +``` +File: schemas/gitmind.txn.meta.schema.json +Purpose: Enforce a minimal, durable contract for transaction audit data. +Format: JSON (CBOR-encoded on disk is fine; schema targets the JSON model). +``` + +### JSON Schema (Draft 2020-12) + +```json +{ + "$id": "https://gitmind.dev/schemas/gitmind.txn.meta.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "GitMind Transaction Metadata", + "type": "object", + "additionalProperties": false, + "required": [ + "version", + "txn_id", + "author", + "branch", + "base_oid", + "created_at", + "status", + "edge_count", + "hash" + ], + "properties": { + "version": { + "description": "Schema payload version for meta format.", + "type": "string", + "pattern": "^1\\.0\\.0$" + }, + "txn_id": { + "description": "ULID for uniqueness and sortability.", + "type": "string", + "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$" + }, + "author": { + "type": "object", + "additionalProperties": false, + "required": ["name", "email"], + "properties": { + "name": { "type": "string", "minLength": 1, "maxLength": 200 }, + "email": { "type": "string", "format": "email", "maxLength": 320 } + } + }, + "branch": { + "description": "Target journal branch (e.g., main).", + "type": "string", + "minLength": 1, + "maxLength": 200, + "pattern": "^[A-Za-z0-9._/\\-]+$" + }, + "base_oid": { + "description": "OID of refs/gitmind/edges/ at txn start.", + "type": "string", + "pattern": "^[0-9a-f]{7,40}$" + }, + "created_at": { + "description": "ISO8601 UTC timestamp when txn started.", + "type": "string", + "format": "date-time" + }, + "applied_at": { + "description": "ISO8601 UTC timestamp when txn applied (nullable until applied).", + "type": ["string", "null"], + "format": "date-time" + }, + "aborted_at": { + "description": "ISO8601 UTC timestamp when txn aborted (nullable until aborted).", + "type": ["string", "null"], + "format": "date-time" + }, + "status": { + "description": "Lifecycle state.", + "type": "string", + "enum": ["pending", "applied", "aborted"] + }, + "edge_count": { + "description": "Number of staged edges in txn.", + "type": "integer", + "minimum": 0 + }, + "hash": { + "description": "Deterministic hash of staged edge payloads.", + "type": "string", + "pattern": "^sha256-[0-9a-f]{64}$" + }, + "notes": { + "description": "Optional human note.", + "type": "string", + "maxLength": 1000 + }, + "namespace": { + "description": "User/actor namespace for isolation (recommended).", + "type": "string", + "maxLength": 120, + "pattern": "^[A-Za-z0-9._/\\-]+$" + }, + "provenance": { + "description": "Where this txn originated.", + "type": "object", + "additionalProperties": false, + "required": ["tool", "tool_version"], + "properties": { + "tool": { "type": "string", "minLength": 1, "maxLength": 120 }, + "tool_version": { "type": "string", "minLength": 1, "maxLength": 60 }, + "host": { "type": "string", "maxLength": 255 }, + "pid": { "type": "integer", "minimum": 0 } + } + }, + "replay_policy": { + "description": "Behavior if base moved or duplicates appear.", + "type": "string", + "enum": ["strict", "skip-duplicates", "best-effort"], + "default": "strict" + }, + "retention": { + "description": "Archival preference after apply/abort.", + "type": "string", + "enum": ["keep", "prune", "days-7", "days-30", "days-90"], + "default": "days-30" + }, + "labels": { + "description": "Freeform labels for filtering/search.", + "type": "array", + "items": { "type": "string", "minLength": 1, "maxLength": 40 }, + "maxItems": 16, + "uniqueItems": true + } + }, + "allOf": [ + { + "if": { "properties": { "status": { "const": "pending" } } }, + "then": { + "properties": { "applied_at": { "type": "null" }, "aborted_at": { "type": "null" } } + } + }, + { + "if": { "properties": { "status": { "const": "applied" } } }, + "then": { + "required": ["applied_at"], + "properties": { "aborted_at": { "type": "null" } } + } + }, + { + "if": { "properties": { "status": { "const": "aborted" } } }, + "then": { + "required": ["aborted_at"], + "properties": { "applied_at": { "type": "null" } } + } + } + ] +} +``` + +### Deterministic Hash Rule (normative) + +``` +hash = "sha256-" + sha256_hex(concat(edge_hashes_sorted)) +``` + +• edge_hashes_sorted: list of sha256_hex of each staged edge’s canonical JSON payload (no whitespace; keys sorted lexicographically; UTF-8). +• Sort the per-edge hashes lexicographically before concatenation. +• This makes hash independent of edge insertion order. + +### Invariants (must hold) + +• I1: txn_id is immutable after creation. +• I2: If status == "applied", applied_at != null and aborted_at == null. +• I3: If status == "aborted", aborted_at != null and applied_at == null. +• I4: created_at <= applied_at|aborted_at (when present). +• I5: edge_count equals the number of staged edge commits under pending//edges/. +• I6: base_oid must have been the tip of refs/gitmind/edges/ at created_at (enforced at txn start). +• I7: hash must verify against current staged edges before apply. +• I8 (namespace, recommended): pending// is the ref path if namespace is set; otherwise pending/. + +### Validation Strategy + +• Schema validation: run on txn start, txn show, and always on txn apply. +• Hash verification: recompute hash from staged edges before apply. +• Base guard: on apply, if HEAD(edges/) != base_oid, follow replay_policy: +• strict → fail with GM_TXN_BASE_MOVED +• skip-duplicates → replay non-duplicate edges; fail on schema errors +• best-effort → attempt replay; warn on skips; still fail on invariants + +### Error Codes (don’t be cute; be specific) + +• GM_TXN_SCHEMA_INVALID – meta failed JSON Schema validation +• GM_TXN_HASH_MISMATCH – staged edges don’t match hash +• GM_TXN_BASE_MOVED – fast-forward check failed (and policy is strict) +• GM_TXN_REPLAY_CONFLICT – duplicate ULIDs or schema incompatibility during replay +• GM_TXN_STATUS_IMMUTABLE – illegal status transition (e.g., applied → pending) +• GM_TXN_ID_IMMUTABLE – attempt to mutate txn_id +• GM_TXN_ABORT_APPLIED – refuse to abort an already-applied txn +• GM_TXN_NOT_FOUND – unknown txn_id / missing ref subtree + +#### Example Valid Meta (compact JSON) + +```json +{ + "version": "1.0.0", + "txn_id": "01J7A3S5J0B9QHY1TG8RZVZPWF", + "author": { "name": "J. Kirby Ross", "email": "james@flyingrobots.dev" }, + "branch": "main", + "base_oid": "9f3c12a", + "created_at": "2025-09-17T20:41:12Z", + "applied_at": null, + "aborted_at": null, + "status": "pending", + "edge_count": 2, + "hash": "sha256 -0f9f1c2d5e1b4a39d8ff1a2c3b4c5d6e7f8a9b0c1d2e3f405162738495aabbcc".replace(" ", ""), + "notes": "Initial draft links A→B, B→C", + "namespace": "flyingrobots", + "provenance": { "tool": "git-mind-cli", "tool_version": "0.7.0", "host": "seattle-dev-01", "pid": 23142 }, + "replay_policy": "strict", + "retention": "days-30", + "labels": ["preview", "ai-seeded"] +} +``` + +### CLI Enforcement Hooks (quick spec) + +• txn start +• Create meta with status=pending; validate against schema. +• txn show +• Validate meta; recalc edge_count; warn on mismatch. +• txn apply +• Validate meta → verify hash → base guard (+ policy) → apply → set applied_at, status=applied, move to applied/ or GC per retention. +• txn abort +• Refuse if status=applied; otherwise set status=aborted, aborted_at=now, then prune per retention. + +### Back/Forward Compatibility + +• Freeze this as version: "1.0.0". +• Future changes must bump version and keep 1.x readers tolerant of additional fields (but never missing requireds). +• Don’t repurpose fields. Add new ones and bump. + +Up next, I write a 10-line ajv/jq preflight snippet for make ci-local that validates meta + recomputes the deterministic hash before letting tests run. + +## CI preflight snippet you can wire into make ci-local (or any test harness) + +It validates meta against the JSON Schema and recomputes the deterministic hash before proceeding. + +### scripts/check-txn-meta.sh + +```bash +#!/usr/bin/env bash +set -euo pipefail + +META_FILE="$1" +EDGES_DIR="$2" # path to staged edges JSON/CBOR payloads + +SCHEMA="schemas/gitmind.txn.meta.schema.json" + +# 1. JSON Schema validation +echo "[gm-txn] validating $META_FILE against $SCHEMA..." +npx ajv-cli validate -s "$SCHEMA" -d "$META_FILE" --spec=draft2020 > /dev/null + +# 2. Recompute deterministic hash of staged edges +TMP_HASHES=$(mktemp) +for edge in "$EDGES_DIR"/*.json; do + jq -cS . "$edge" | sha256sum | awk '{print $1}' >> "$TMP_HASHES" +done + +SORTED=$(sort "$TMP_HASHES" | tr -d '\n') +RECALC="sha256-$(echo -n "$SORTED" | sha256sum | awk '{print $1}')" +rm "$TMP_HASHES" + +# 3. Compare with meta.hash +META_HASH=$(jq -r .hash "$META_FILE") + +if [[ "$META_HASH" != "$RECALC" ]]; then + echo "[gm-txn] ERROR: hash mismatch" + echo " expected: $META_HASH" + echo " actual: $RECALC" + exit 1 +fi + +echo "[gm-txn] OK: $META_FILE valid and hash verified" +``` + +### Usage + +```bash +# Validate pending txn 01J7A3S5... +./scripts/check-txn-meta.sh \ + .git/refs/gitmind/pending/01J7A3S5J0B9QHY1TG8RZVZPWF/meta \ + .git/refs/gitmind/pending/01J7A3S5J0B9QHY1TG8RZVZPWF/edges/main +``` + +This ensures every transaction in CI: + +• Conforms to schema (ajv-cli). +• Matches its deterministic payload hash (order-independent). + +Here’s the hook wiring so nobody “helpfully” yeets half-baked transactions upstream. Drop-in scripts, minimal ceremony, maximal guardrails. + +## Git Hooks for Sandbox Transactions + +### 1) Enable local hooks once + +```bash +git config core.hooksPath .githooks +mkdir -p .githooks scripts +``` + +Assumes you already saved the earlier scripts/check-txn-meta.sh. + +### 2) Client-side pre-push + +Block pushing pending/ by default and validate any txn meta/hash if someone does opt-in. + +#### File: .githooks/pre-push (chmod +x) + +```bash +#!/usr/bin/env bash +set -euo pipefail + +REMOTE_NAME="$1"; REMOTE_URL="$2" || true + +# Env switches: +# PUSH_PENDING=1 -> allow pushing refs/gitmind/pending/* +# GM_TXN_STRICT=1 -> also validate meta/hash for applied txns by scanning push set +# Paths: +CHECK_SCRIPT="scripts/check-txn-meta.sh" + +echo "[pre-push] scanning refs about to push to $REMOTE_NAME ($REMOTE_URL)" + +# read lines: +PENDING_SEEN=0 +APPLIED_SEEN=0 +while read -r LOCAL_SHA LOCAL_REF REMOTE_SHA REMOTE_REF; do + [[ -z "${LOCAL_REF:-}" ]] && continue + + if [[ "$LOCAL_REF" =~ ^refs/gitmind/pending/ ]]; then + PENDING_SEEN=1 + if [[ "${PUSH_PENDING:-0}" != "1" ]]; then + echo "[pre-push] ERROR: refusing to push pending transaction refs:" + echo " $LOCAL_REF" + echo " (set PUSH_PENDING=1 to intentionally publish pending refs for review)" + exit 1 + fi + + # Validate meta/hash for this pending txn + TXN_PATH=".git/$(echo "$LOCAL_REF" | sed 's#^refs/##')" + META_FILE="$TXN_PATH/meta" + # find branch dir under edges/* + EDGES_DIR=$(find "$TXN_PATH/edges" -maxdepth 1 -type d | tail -n +2 | head -n 1 || true) + if [[ -f "$META_FILE" && -n "${EDGES_DIR:-}" ]]; then + bash "$CHECK_SCRIPT" "$META_FILE" "$EDGES_DIR" + else + echo "[pre-push] ERROR: pending txn missing meta or edges: $LOCAL_REF" + exit 1 + fi + fi + + if [[ "$LOCAL_REF" =~ ^refs/gitmind/edges/ ]]; then + APPLIED_SEEN=1 + fi +done + +# Optional: block direct writes to canonical journal unless explicitly acknowledged +if [[ "$APPLIED_SEEN" == "1" && "${ALLOW_CANONICAL_WRITES:-0}" != "1" ]]; then + echo "[pre-push] ERROR: direct pushes to refs/gitmind/edges/* are disabled." + echo "Use: git mind txn apply (or set ALLOW_CANONICAL_WRITES=1 if you absolutely must)." + exit 1 +fi + +echo "[pre-push] OK" +exit 0 +``` + +Typical flows: + +• Normal devs: push code branches only → hook does nothing. +• Review pending txn (rare): + +```bash +PUSH_PENDING=1 git push origin refs/gitmind/pending/01J...:refs/gitmind/pending/01J... +``` + +Meta/hash validated before it leaves your laptop. + +• Hard override (discouraged): + +```bash +ALLOW_CANONICAL_WRITES=1 git push origin refs/gitmind/edges/main +``` + +###3) Server-side pre-receive + +Final backstop. Reject pending refs outright (unless you run a special “draft” remote), and require a provenance trailer on journal commits so only txn apply can touch canonical refs. + +#### Commit trailer chosen: Gm-Txn: (ULID) + +Have your git mind txn apply write commits with: + +```bash +Gm-Txn: 01J7A3S5J0B9QHY1TG8RZVZPWF +Gm-Apply: git-mind-cli/0.7.0 +``` + +#### File (server repo): .git/hooks/pre-receive (chmod +x) + +```bash +#!/usr/bin/env bash +set -euo pipefail + +reject() { echo "[pre-receive] $1" >&2; exit 1; } + +while read -r OLD NEW REF; do + # Block pending refs by default + if [[ "$REF" =~ ^refs/gitmind/pending/ ]]; then + reject "pending refs are not accepted on this remote: $REF" + fi + + # Enforce provenance on canonical journal + if [[ "$REF" =~ ^refs/gitmind/edges/ ]]; then + # New branch or update; scan new commits + RANGE="$OLD..$NEW" + # Allow branch creation (OLD == 0000...) — still scan NEW + if [[ "$OLD" == "0000000000000000000000000000000000000000" ]]; then + RANGE="$NEW" + fi + + # Every new commit must carry Gm-Txn trailer + # (git log --format='%(trailers:key=Gm-Txn)' prints trailer value if present) + MISSING=$(git rev-list "$RANGE" | while read -r C; do + T=$(git log -1 --pretty='%(trailers:key=Gm-Txn,separator=%x0a)' "$C") + [[ -z "$T" ]] && echo "$C" + done) + + if [[ -n "$MISSING" ]]; then + echo "[pre-receive] ERROR: commits to $REF without Gm-Txn trailer:" + echo "$MISSING" + echo "Only 'git mind txn apply' is allowed to write canonical journals." + exit 1 + fi + fi +done + +exit 0 +``` + +If you host on a shared server, put this into your server-side hook (bare repo) so the policy is universal. + +### 4) Optional: update hook to archive applied txns + +When a canonical journal moves forward with a valid trailer, you can auto-archive the matching pending txn under refs/gitmind/applied/ (if the client didn’t do it). + +#### File (server): .git/hooks/update (chmod +x) + +Called once per ref update: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +REF="$1"; OLD="$2"; NEW="$3" + +if [[ "$REF" =~ ^refs/gitmind/edges/ ]]; then + # Extract the last commit’s Gm-Txn + TXN=$(git log -1 --pretty='%(trailers:key=Gm-Txn,valueonly)' "$NEW" | tail -n1) + if [[ -n "$TXN" ]]; then + PENDING="refs/gitmind/pending/$TXN" + APPLIED="refs/gitmind/applied/$TXN" + if git show-ref --verify --quiet "$PENDING"; then + echo "[update] archiving $PENDING -> $APPLIED" + # Create lightweight namespace under applied if not present + git update-ref "$APPLIED" "$(git rev-parse "$PENDING")" + # Optionally delete pending + git update-ref -d "$PENDING" + fi + fi +fi + +exit 0 +``` + +### 5) Bonus: local pre-commit sanity (cheap win) + +Catch accidental direct writes to journal files in the worktree (if any) and keep txn meta in sync. + +#### File: .githooks/pre-commit (chmod +x) + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Block accidental edits to journal files in repo (if they exist in worktree) +BAD=$(git diff --cached --name-only -- \ + 'refs/gitmind/edges/*' 'refs/gitmind/pending/*' 2>/dev/null || true) +if [[ -n "$BAD" ]]; then + echo "[pre-commit] ERROR: do not commit refs/* paths via worktree." + echo "Use 'git mind' CLI to mutate journals/transactions." + exit 1 +fi + +# If a txn is active, keep meta.edge_count honest (best-effort) +if [[ -n "${GITMIND_TXN_ID:-}" ]]; then + TXN_DIR=".git/refs/gitmind/pending/${GITMIND_TXN_ID}" + META="$TXN_DIR/meta" + if [[ -f "$META" ]]; then + EDGES_DIR=$(find "$TXN_DIR/edges" -maxdepth 1 -type d | tail -n +2 | head -n 1 || true) + if [[ -n "${EDGES_DIR:-}" && -d "$EDGES_DIR" ]]; then + COUNT=$(ls -1 "$EDGES_DIR"/*.json 2>/dev/null | wc -l | tr -d ' ') + jq --argjson n "$COUNT" '.edge_count=$n' "$META" > "$META.tmp" && mv "$META.tmp" "$META" + fi + fi +fi +``` + +### 6) Policy TL;DR (communicate this in your RFC) + +• You cannot push pending/ by default. Opt-in with PUSH_PENDING=1 for review-only remotes. +• You cannot push edges/* directly. Only commits created by git mind txn apply (with Gm-Txn: trailer) are accepted server-side. +• All pending txns are meta+hash validated before they’re allowed to leave a machine. +• Applied txns are auto-archived server-side when possible; otherwise clients should move pending → applied. + +This setup makes the safe path the easy path: + • Stage in a sandbox, preview, apply with a provenance trailer, and let the server archive. + • Anyone trying to sneak around the process gets a fast, loud failure. Good. diff --git a/docs/talk-shop/centralized-cbor-keys-and-public-apis.md b/docs/talk-shop/centralized-cbor-keys-and-public-apis.md index 8edf0dc9..598ca1ff 100644 --- a/docs/talk-shop/centralized-cbor-keys-and-public-apis.md +++ b/docs/talk-shop/centralized-cbor-keys-and-public-apis.md @@ -1,3 +1,13 @@ +--- +title: Centralized CBOR Keys and Public Encoders +description: Explorations and design discussions. +audience: [contributors] +domain: [project] +tags: [discussion] +status: archive +last_updated: 2025-09-15 +--- + # Centralized CBOR Keys and Public Encoders Wire formats drift when every module invents its own constants. We centralized CBOR field keys in a single header and exposed public encode/decode helpers. That small move pays huge dividends: writers are consistent, readers are simpler, and schema evolution becomes a surgical change—not a scavenger hunt. It also makes fuzzing and differential testing easier—one place to feed, many places to validate. diff --git a/docs/talk-shop/deterministic-merges-crdt-advice.md b/docs/talk-shop/deterministic-merges-crdt-advice.md index d5464e80..7e6880b0 100644 --- a/docs/talk-shop/deterministic-merges-crdt-advice.md +++ b/docs/talk-shop/deterministic-merges-crdt-advice.md @@ -1,3 +1,13 @@ +--- +title: Deterministic Merges with CRDT Advice +description: Explorations and design discussions. +audience: [contributors] +domain: [project] +tags: [discussion] +status: archive +last_updated: 2025-09-15 +--- + # Deterministic Merges with CRDT Advice When knowledge lives in Git, merges need to be predictable. We model edges as commits (append‑only) and layer “advice” with a minimal set of CRDT rules: Last‑Writer‑Wins (LWW) for scalars and OR‑Set for collections. Two people (or a person and an AI) can explore semantics on branches and then converge without surprises. No hidden conflict resolvers, no opaque heuristics—just deterministic replay on top of Git’s own merge model. diff --git a/docs/talk-shop/hardening-mindset.md b/docs/talk-shop/hardening-mindset.md index 44c4164a..b339f2b9 100644 --- a/docs/talk-shop/hardening-mindset.md +++ b/docs/talk-shop/hardening-mindset.md @@ -1,3 +1,13 @@ +--- +title: The Hardening Mindset: Safety as a Feature +description: Explorations and design discussions. +audience: [contributors] +domain: [project] +tags: [discussion] +status: archive +last_updated: 2025-09-15 +--- + # The Hardening Mindset: Safety as a Feature Great semantics deserve great engineering. We treat safety work—bounds‑checked formatting, consistent error codes, and growing tests—as product features. `gm_snprintf` replaces risky `snprintf`; GM_ERR_* codes show up everywhere; and tests cover the paths users actually hit (journal read/write, cache fan‑in/out, equality semantics). The goal isn’t “no crashes,” it’s graceful failure with debuggable signals. diff --git a/docs/talk-shop/journal-first-cbor-commits.md b/docs/talk-shop/journal-first-cbor-commits.md index 85415635..60095181 100644 --- a/docs/talk-shop/journal-first-cbor-commits.md +++ b/docs/talk-shop/journal-first-cbor-commits.md @@ -1,3 +1,13 @@ +--- +title: Journal‑First CBOR Commits: Time‑Travel You Can Trust +description: Explorations and design discussions. +audience: [contributors] +domain: [project] +tags: [discussion] +status: archive +last_updated: 2025-09-15 +--- + # Journal‑First CBOR Commits: Time‑Travel You Can Trust Most systems treat metadata as an afterthought; we made it the source of truth. In git‑mind, every semantic edge is an append‑only CBOR payload committed under a dedicated ref (`refs/gitmind/edges/`). That journal design buys us three things at once: immutability (no in‑place mutation, ever), auditability (every change has authorship, time, and diff), and actual time‑travel (check out an old commit and you get the semantics from that moment). It’s just Git, which means the transport, permission model, replication, and conflict semantics are already battle‑tested. diff --git a/docs/talk-shop/oid-first-migration.md b/docs/talk-shop/oid-first-migration.md index af97d818..3316210c 100644 --- a/docs/talk-shop/oid-first-migration.md +++ b/docs/talk-shop/oid-first-migration.md @@ -1,3 +1,13 @@ +--- +title: OID‑First Migration: Future‑Proof by Design +description: Explorations and design discussions. +audience: [contributors] +domain: [project] +tags: [discussion] +status: archive +last_updated: 2025-09-15 +--- + # OID‑First Migration: Future‑Proof by Design Relying on raw 20‑byte SHA‑1 arrays is a dead end. git‑mind migrates interfaces to `git_oid` (our `gm_oid_t`) across the core, so the code is agnostic to the underlying hash algorithm. Equality, hashing, and cache sharding all operate on OIDs, not legacy byte blobs. That shift eliminates subtle bugs (like mismatched sizes or comparisons) and ensures we don’t care whether the repo is SHA‑1, SHA‑256, or what comes next. diff --git a/docs/templates/code-review-feedback.md b/docs/templates/code-review-feedback.md new file mode 100644 index 00000000..b1fc1f07 --- /dev/null +++ b/docs/templates/code-review-feedback.md @@ -0,0 +1,100 @@ +--- +title: {filename} +description: Preserved review artifacts and rationale. +audience: [contributors] +domain: [quality] +tags: [review] +status: archive +--- + +# Code Review Feedback + +| Date | Agent | SHA | Branch | Branch URL | PR URL | +| ------ | ------- | ------- | -------- | --------------------- | ---------- | +| {date} | {agent} | `{sha}` | {branch} | `{github_branch_url}` | `{pr_url}` | + +## Instructions + +Please carefully consider each of the following feedback items, collected from a GitHub code review. + +Please act on each item by fixing the issue, or rejecting the feedback. Please update this document and fill out the information below each feedback item by replacing the text surrounded by curly braces. + +### Accepted Feedback Template + +Please use the following template to record your acceptance. + +```markdown + +> [!NOTE]- **Accepted** +> | Confidence | Remarks | +> |------------|---------| +> | {confidence_score_out_of_10} | {confidence_rationale} | +> +> ## Lesson Learned +> +> {lesson} +> +> ## What did you do to address this feedback? +> +> {what_you_did} +> +> ## Regression avoidance strategy +> +> {regression_avoidance_strategy} +> +> ## Notes +> +> {any_additional_context_or_say_none} + +``` + +### Rejected Feedback Template + +Please use the following template to record your rejections. + +```markdown + +> [!CAUTION]- **Rejected** +> | Confidence | Remarks | +> |------------|---------| +> | {confidence_score_out_of_10} | {confidence_rationale} | +> +> ## Rejection Rationale +> +> {rationale} +> +> ## What did you do instead? +> +> {what_you_did} +> +> ## Tradeoffs considered +> +> {pros_and_cons} +> +> ## What would make you change your mind +> +> {change_mind_conditions} +> +> ## Future Plans +> +> {future_plans} + +``` + +--- + +## CODE REVIEW FEEDBACK + +> How to use: copy one of the fenced templates above, paste under each feedback item, and replace all `{placeholders}`. + +The following section contains the feedback items, extracted from the code review linked above. Please read each item and respond with your decision by injecting one of the two above templates beneath the feedback item. + +### {feedback_item_title} + +## Conclusion + +| Accepted | Rejected | Remarks | +|-----------|----------|---------| +| {accepted_count} | {rejected_count} | {remarks} | + +{comments} diff --git a/docs/testing/Test_Plan.md b/docs/testing/Test_Plan.md index 0b0584fb..519c896a 100644 --- a/docs/testing/Test_Plan.md +++ b/docs/testing/Test_Plan.md @@ -1,3 +1,13 @@ +--- +title: Test Plan +description: Strategy and structure for unit, integration, and E2E testing. +audience: [developers, contributors] +domain: [testing] +tags: [testing] +status: draft +last_updated: 2025-09-15 +--- + # Test Plan Table of Contents diff --git a/docs/tools/PR_Workflow_Factoring.md b/docs/tools/PR_Workflow_Factoring.md new file mode 100644 index 00000000..9d0e4159 --- /dev/null +++ b/docs/tools/PR_Workflow_Factoring.md @@ -0,0 +1,221 @@ +--- +title: Factoring PR Workflow to a Standalone Repo +description: Step‑by‑step guide to extract our PR seeding + worksheet + auto‑reply workflow into its own reusable repository. +audience: [maintainers, contributors] +domain: [tooling] +tags: [review, github-actions, automation] +status: draft +last_updated: 2025-09-16 +--- + +# Factoring PR Workflow to a Standalone Repo + +## Overview + +This guide captures how to factor our “PR feedback → worksheet → auto-reply” workflow into a separate, reusable repository that any project can consume with minimal setup. + +The goal is a portable, easy‑install review pipeline that works great for both AI agents and humans: seed a worksheet from PR comments, fill in Accepted/Rejected with rationale, and let the bot reply and (optionally) resolve threads. + +## Components to Extract + +Core (to externalize): +- Scripts + - `tools/review/seed_feedback_from_github.py` + - `tools/review/check_worksheets.py` + - `tools/review/apply_feedback_to_github.py` +- Workflows + - `.github/workflows/auto-seed-review.yml` — seed worksheet on PR events + - `.github/workflows/apply-feedback.yml` — reply on push of updated worksheets +- Hooks & config (optional but helpful) + - Pre‑push gate: `.githooks/pre-push` changes (template) + - Pre‑commit hook: worksheet checker and curation (templates) + - Review config: `.coderabbit.yml` suggestions + - PR template: `.github/pull_request_template.md` guidance + - Docs: `docs/tools/Review_Seeding.md` user guide + +Nice‑to‑have extras: +- GH Labeler: `.github/labeler.yml` + docs‑only workflow +- Curator (AGENTS‑style): `tools/agents/curate_recent.py` (not required for the PR flow) + +## Target Repo Layout (Example) + +``` +review-buddy/ + README.md + LICENSE + scripts/ + seed_feedback.py + apply_feedback.py + check_worksheets.py + .github/ + workflows/ + seed-review.yml # reusable workflow + apply-feedback.yml # reusable workflow + action/ + apply-feedback/action.yml # composite Action for replies + seed-review/action.yml # composite Action for seeding + templates/ + pre-push # sample pre-push gate + pre-commit-config.yaml # sample pre-commit additions + pull_request_template.md + coderabbit.yml + docs/ + GETTING_STARTED.md + ADVANCED.md +``` + +Notes: +- Prefer both “reusable workflows” (callable with `workflow_call`) and “composite actions” for flexibility. +- Scripts live in `scripts/` and are referenced by the composite actions. + +## Extraction Steps (from this repo) + +1) Create the new repo, e.g., `neuroglyph/review-buddy` (MIT/Apache‑2.0 preferred). +2) Copy scripts into `scripts/` and update imports and paths to be repo‑relative. +3) Convert current workflows to reusable workflows: + - `on: workflow_call` + - Inputs: `pr_number`, `seed_to_branch`, toggles +4) Create composite actions (`action/*/action.yml`) that shell out to the Python scripts for easy inclusion in non‑reusable workflows. +5) Add docs (`GETTING_STARTED.md`, `ADVANCED.md`) with install steps. +6) Add an example consuming project in `examples/` (optional). + +## Consuming Repo Setup + +Option A — Reusable workflows: +``` +# .github/workflows/seed-review.yml +name: Seed Review Doc +on: + pull_request_target: + types: [opened, reopened, synchronize] + +jobs: + seed: + uses: neuroglyph/review-buddy/.github/workflows/seed-review.yml@vX.Y.Z + secrets: inherit +``` + +``` +# .github/workflows/apply-feedback.yml +name: Apply Feedback Replies +on: + push: + branches-ignore: [ main ] + paths: [ 'docs/code-reviews/**.md' ] + +jobs: + apply: + uses: neuroglyph/review-buddy/.github/workflows/apply-feedback.yml@vX.Y.Z + secrets: inherit +``` + +Option B — Composite actions (per‑job): +``` +jobs: + seed: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: neuroglyph/review-buddy/action/seed-review@vX.Y.Z + with: + pr: ${{ github.event.number }} +``` + +Secrets required (at minimum): +- `GITHUB_TOKEN` (provided by Actions) +- Optional fork push PAT: `GITMIND_PR_WORKSHEET_BOT` (or rename for the external repo) + +## Pre‑push / Pre‑commit Templates + +Pre‑push gate (blocks if worksheets contain `{response}` or undecided sections): +- Provide a `templates/pre-push` script and document symlink/install. + +Pre‑commit templates: +- Provide a stanza to insert into `.pre-commit-config.yaml` to run the worksheet checker on docs changes. + +## Configuration Surface + +Top‑level `review-buddy.yml` (in consuming repo): +``` +worksheet: + dir: docs/code-reviews + accepted_marker: "[!note]- **Accepted**" + rejected_marker: "[!CAUTION]- **Rejected**" + require_decision_for_all: true + +seeding: + seed_to_branch: auto # auto | artifact_only + bot_secret: REVIEW_BOT_PAT + maintainers_edit_required: true + +apply: + reply_accepted: true + reply_rejected: true + resolve_accepted: false # GraphQL; can be enabled later +``` + +The composite actions / reusable workflows should respect these conventions via environment variables and default values. + +## Versioning + Release + +- Tag releases `vX.Y.Z`. +- Changelog: list breaking changes for inputs/outputs. +- Provide a matrix of supported GitHub Runners and Python versions. + +## “HOLY SHIT THIS IS AMAZING” Upgrades + +Make it delightful for agents and humans: + +1) GraphQL Auto‑Resolve + - After replying on accepted items, resolve review threads via GraphQL mutation. + - Map worksheet sections to original thread IDs; dry‑run mode for safety. + +2) PR Dashboard Comment + - Post a summary comment on PR with counts: total, accepted, rejected, undecided; link to worksheet. + - Update the comment on each push or worksheet change. + +3) ChatOps Commands + - `/seed` to regenerate worksheet; `/apply` to apply replies; `/resolve-accepted` to resolve threads. + - Gate commands by role (maintainer only) and label. + +4) Web‑Preview of Worksheet + - Serve a GitHub Pages or Action artifact as a styled HTML view of the worksheet (TOC, filters, search). + - Provide a “focus mode” for reviewers to skim Accepted/Rejected quickly. + +5) Multi‑Repo Templates + - A single “org” repo stores default templates (.coderabbit.yml, PR template, hooks). Repos opt‑in or override. + +6) Deep CodeRabbit Integration + - Parse CR’s review metadata (if accessible) to group comments by rule/pattern. + - Suggest fix‑it snippets and patch stacks; apply suggestions in a fresh branch via a bot. + +7) Quality Signals + Badges + - Expose a JSON “review‑health” summary: % accepted, time‑to‑address, undecided count. + - CI gate: fail if undecided > threshold on “ready to merge” label. + +8) Agent‑First Ergonomics + - Provide a tiny API: `seed(pr)`, `apply(pr)`, `resolve(pr)` that agents can call with one line. + - Emit machine‑readable logs with stable keys (decision=accepted/rejected, url, title, section). + +9) Installer Script + - `npx review-buddy init` to add workflows, templates, and secrets scaffolding to a repo with prompts. + +10) Secure Bot Options + - Ship a GitHub App (least privilege) as an alternative to PAT. + - Document org‑wide vs repo‑specific secret strategies. + +## Migration Checklist (from this repo) + +- [ ] Create `neuroglyph/review-buddy` with scripts, workflows, actions, and docs. +- [ ] Replace in‑repo scripts with references to the external Action/workflows. +- [ ] Update docs: point to `GETTING_STARTED.md` in the new repo. +- [ ] Keep a compatibility shim for a few releases (graceful deprecation). + +## Getting to “Meh → Amazing” + +Deliver smooth on‑ramp + high trust: +- One‑command install; sensible defaults; crisp errors. +- Great docs with copy‑paste snippets and screenshots. +- Safety: dry‑run modes, explicit labels to enable writing. +- Telemetry: text summaries + JSON for automation. +- Extensibility: template markers for block parsing, configurable decision markers, pluggable resolvers. diff --git a/docs/tools/Review_Seeding.md b/docs/tools/Review_Seeding.md new file mode 100644 index 00000000..94747992 --- /dev/null +++ b/docs/tools/Review_Seeding.md @@ -0,0 +1,114 @@ +--- +title: Review Seeding and Auto‑Worksheet +description: How PR review comments are harvested into a structured worksheet and committed back to the PR branch. +audience: [contributors, maintainers] +domain: [quality] +tags: [review, automation] +status: stable +last_updated: 2025-09-16 +--- + +# Review Seeding and Auto‑Worksheet + +> This documents the PR feedback workflow: scraping GitHub PR comments (reviews + discussion), pre‑filling a structured worksheet under `docs/code-reviews/PR/.md`, and committing it back to the PR branch. + +## Overview + +- Script: `tools/review/seed_feedback_from_github.py` +- Make target: `make seed-review PR=` +- Auto workflows: + - Same‑repo PRs: auto‑commit seeded worksheet to the PR branch + - Fork PRs: attempt push with bot PAT; else upload artifact + comment +- Review config: + - `.coderabbit.yml` tunes CodeRabbit (summary‑first, caps, doc filters) + - `.github/pull_request_template.md` guides reviewers + - Docs‑only PRs auto‑labeled via `.github/labeler.yml` + +## Local Usage + +1. Create a GitHub token and export it: + - Fine‑grained token, minimal scopes: Pull Requests: Read; Issues: Read; Metadata: Read + - Classic token alternative: `repo` (read) is sufficient + - macOS/Linux: `export GITHUB_TOKEN=ghp_xxx` +2. Seed a worksheet: + - `make seed-review PR=169` + - Output: `docs/code-reviews/PR169/.md` +3. Fill out `{response}` sections using Accepted/Rejected templates and commit. + +## CI Automation + +### Same‑repo PRs +- Workflow: `.github/workflows/auto-seed-review.yml` +- Trigger: PR opened/reopened/synchronized +- Action: seeds doc and commits directly to the PR branch using `GITHUB_TOKEN`. + +### Fork PRs +- If “Allow edits from maintainers” is enabled and a bot PAT is configured: + - Attempts to commit the seeded doc to the contributor’s branch +- Otherwise: + - Uploads the seeded file as a build artifact + - Adds a PR comment with instructions to copy the file into the branch + +## Bot PAT Configuration (for fork PR pushes) + +Use a Fine‑grained Personal Access Token: +- Repository access: Only select repositories → select this repo +- Repository permissions (minimum): + - Contents: Read and write + - Pull requests: Read and write + - Issues: Read and write + - Metadata: Read +- Add the token to repo secrets as: `GITMIND_PR_WORKSHEET_BOT` +- Ensure contributors keep “Allow edits from maintainers” enabled on PRs + +The workflow will use `GITMIND_PR_WORKSHEET_BOT` to push the seeded doc to fork PR branches when allowed. If push fails (edits disabled), the artifact+comment path is used. + +## Manual Workflow + +- Workflow: `.github/workflows/seed-review.yml` +- Dispatch from Actions with input `pr=` +- Produces an artifact (folder `review-seed`) with the generated worksheet + +## Reply Workflow Validation + +When updating templates or automation, run a quick readability check: + +1. Open a scratch PR (or reuse a maintenance PR) and ensure it has at least one unresolved review thread. +2. Update the worksheet locally, then run `make apply-feedback FILES="docs/code-reviews/PR/*.md"` so the workflow replies on your behalf (requires `GITHUB_TOKEN`). +3. Inspect the GitHub conversation to confirm Markdown renders correctly (callouts collapsed, bullets aligned) and capture a screenshot for the changelog if formatting changed. +4. If anything regresses, roll back the worksheet commit and iterate locally before retrying. + +## Review Document Structure + +- Location: `docs/code-reviews/PR/.md` +- Contains one heading per feedback item with a fenced text block +- Replace `{response}` with: + - Accepted template (with lesson + prevention), or + - Rejected template (with rationale + tradeoffs) + +## Best Practices + +- Keep fixes in small, targeted commits; link commit SHAs into the worksheet +- Prefer rule/pattern references over fragile line numbers (e.g., MD041) +- For docs‑only PRs, rely on summary review; minimize inline nit comments +- Use `make docs-verify` before pushing to keep docs clean + +## Related + +- `.coderabbit.yml` — CodeRabbit tuning +- `.github/pull_request_template.md` — Reviewer guidance +- `.github/workflows/auto-seed-review.yml` — Auto‑seeding logic +- `.github/workflows/label-docs-only.yml` — Auto labeler +- `make docs-verify` — Front matter, link, TOC, title/H1 checks + +## Future Work + +- GitHub App for Worksheet Bot + - Create a GitHub App that contributors can grant access to their fork. The app’s installation token would allow writing the seeded doc directly to fork branches without broad classic PAT scopes. + - Benefits: principle‑of‑least‑privilege, per‑repo install, clean audit trail. + - Work: app manifest, permissions (Contents:RW, PR:RW, Issues:RW, Metadata:Read), and token exchange in the workflow. +- Classic PAT for pushing to forks (if needed) + - Replace fine‑grained PAT with a classic PAT (scope: `public_repo` for public, or `repo` for private) in the secret `GITMIND_PR_WORKSHEET_BOT` to enable pushes to user‑owned forks. + - Trade‑off: broader scope than fine‑grained tokens. +- Auto‑open helper PR to contributor fork + - If push to fork fails, create a helper PR in their fork with the seeded doc. This typically requires permission to fork/cross‑fork and may be restricted. Consider only if contributor experience warrants it. diff --git a/docs/tutorial.md b/docs/tutorial.md index 2a844919..b944338f 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -30,8 +30,8 @@ export GITMIND_SAFETY=off Create a semantic edge between two files (document → code, code → test, etc.). ```bash -$ "$GIT_MIND" link README.md src/main.c --type documents -Created: README.md -[documents]-> src/main.c +$ "$GIT_MIND" link README.md core/src/main.c --type documents + Created: README.md -[documents]-> core/src/main.c ``` More linking examples and options: `docs/cli/gitmind-link.md`. @@ -42,7 +42,7 @@ List edges from a file to see what it’s connected to. ```bash $ "$GIT_MIND" list --from README.md -README.md -> src/main.c (documents) +README.md -> core/src/main.c (documents) ``` Formatting and filters: `docs/cli/gitmind-list.md`. diff --git a/docs/wish-list-features/brain-storms/CLAUDE_DEEP_THOUGHTS_FINAL.md b/docs/wish-list-features/brain-storms/CLAUDE_DEEP_THOUGHTS_FINAL.md index bf190415..c1a0794b 100644 --- a/docs/wish-list-features/brain-storms/CLAUDE_DEEP_THOUGHTS_FINAL.md +++ b/docs/wish-list-features/brain-storms/CLAUDE_DEEP_THOUGHTS_FINAL.md @@ -1,3 +1,13 @@ +--- +title: 🧠💎 CLAUDE'S DEEP THOUGHTS: The Git-Mind Vision Crystallized +description: Feature brainstorms and design sketches. +audience: [contributors] +domain: [project] +tags: [wishlist] +status: archive +last_updated: 2025-09-15 +--- + # 🧠💎 CLAUDE'S DEEP THOUGHTS: The Git-Mind Vision Crystallized _Context remaining: 6%. Time to download the essence._ diff --git a/docs/wish-list-features/brain-storms/CLAUDE_ULTRATHINKS_GITMIND_FUTURE.md b/docs/wish-list-features/brain-storms/CLAUDE_ULTRATHINKS_GITMIND_FUTURE.md index fb92f1ac..6d049635 100644 --- a/docs/wish-list-features/brain-storms/CLAUDE_ULTRATHINKS_GITMIND_FUTURE.md +++ b/docs/wish-list-features/brain-storms/CLAUDE_ULTRATHINKS_GITMIND_FUTURE.md @@ -478,7 +478,7 @@ typedef struct { // Inference API gm_prediction_t *predictions = gm_ml_predict_edges( engine, - "src/auth.c", + "core/src/auth.c", GM_PREDICT_DEPENDENCIES | GM_PREDICT_IMPLEMENTS ); ``` diff --git a/docs/wish-list-features/brain-storms/plugins.md b/docs/wish-list-features/brain-storms/plugins.md index e6b3ab45..ef74a963 100644 --- a/docs/wish-list-features/brain-storms/plugins.md +++ b/docs/wish-list-features/brain-storms/plugins.md @@ -1,3 +1,13 @@ +--- +title: 🔌 Plugin Architecture 2.0: The Ecosystem Revolution +description: Feature brainstorms and design sketches. +audience: [contributors] +domain: [project] +tags: [wishlist] +status: archive +last_updated: 2025-09-15 +--- + # 🔌 Plugin Architecture 2.0: The Ecosystem Revolution ## Idea Overview diff --git a/docs/wish-list-features/plugins2.0/00-summary.md b/docs/wish-list-features/plugins2.0/00-summary.md index 663ff2c0..2eab476c 100644 --- a/docs/wish-list-features/plugins2.0/00-summary.md +++ b/docs/wish-list-features/plugins2.0/00-summary.md @@ -1,3 +1,13 @@ +--- +title: Plugin Architecture 2.0: Git-Mind Ecosystem Revolution +description: Feature brainstorms and design sketches. +audience: [contributors] +domain: [project] +tags: [wishlist] +status: archive +last_updated: 2025-09-15 +--- + # Plugin Architecture 2.0: Git-Mind Ecosystem Revolution ## Overview diff --git a/docs/wish-list-features/plugins2.0/01-requirements.md b/docs/wish-list-features/plugins2.0/01-requirements.md index 030da033..1ba0f824 100644 --- a/docs/wish-list-features/plugins2.0/01-requirements.md +++ b/docs/wish-list-features/plugins2.0/01-requirements.md @@ -1,3 +1,13 @@ +--- +title: Feature Requirements: Plugin Architecture 2.0 +description: Feature brainstorms and design sketches. +audience: [contributors] +domain: [project] +tags: [wishlist] +status: archive +last_updated: 2025-09-15 +--- + # Feature Requirements: Plugin Architecture 2.0 ## Functional Requirements diff --git a/docs/wish-list-features/plugins2.0/02-tech-design.md b/docs/wish-list-features/plugins2.0/02-tech-design.md index 47552c88..7b70bf65 100644 --- a/docs/wish-list-features/plugins2.0/02-tech-design.md +++ b/docs/wish-list-features/plugins2.0/02-tech-design.md @@ -1,3 +1,13 @@ +--- +title: Technical Design: Plugin Architecture 2.0 +description: Feature brainstorms and design sketches. +audience: [contributors] +domain: [project] +tags: [wishlist] +status: archive +last_updated: 2025-09-15 +--- + # Technical Design: Plugin Architecture 2.0 ## Overview diff --git a/docs/wish-list-features/plugins2.0/03-api.md b/docs/wish-list-features/plugins2.0/03-api.md index dc3935a1..59e183ff 100644 --- a/docs/wish-list-features/plugins2.0/03-api.md +++ b/docs/wish-list-features/plugins2.0/03-api.md @@ -1,4 +1,12 @@ -# __API Specification: Plugin Architecture 2.0__ +--- +title: API Specification: Plugin Architecture 2.0 +description: Feature brainstorms and design sketches. +audience: [contributors] +domain: [project] +tags: [wishlist] +status: archive +last_updated: 2025-09-15 +--- # API Specification: Plugin Architecture 2.0 diff --git a/docs/wish-list-features/plugins2.0/04-test-plan.md b/docs/wish-list-features/plugins2.0/04-test-plan.md index 0b5cf367..0855926d 100644 --- a/docs/wish-list-features/plugins2.0/04-test-plan.md +++ b/docs/wish-list-features/plugins2.0/04-test-plan.md @@ -1,3 +1,13 @@ +--- +title: Test Plan: Plugin Architecture 2.0 +description: Feature brainstorms and design sketches. +audience: [contributors] +domain: [project] +tags: [wishlist] +status: archive +last_updated: 2025-09-15 +--- + # Test Plan: Plugin Architecture 2.0 ## Objective diff --git a/docs/wish-list-features/plugins2.0/05-security.md b/docs/wish-list-features/plugins2.0/05-security.md index b1cd9cfa..1bf02f3d 100644 --- a/docs/wish-list-features/plugins2.0/05-security.md +++ b/docs/wish-list-features/plugins2.0/05-security.md @@ -1,3 +1,13 @@ +--- +title: __🔒 Security Review: Plugin Architecture 2.0__ +description: Feature brainstorms and design sketches. +audience: [contributors] +domain: [project] +tags: [wishlist] +status: archive +last_updated: 2025-09-15 +--- + # __🔒 Security Review: Plugin Architecture 2.0__ # Security Review: Plugin Architecture 2.0 diff --git a/include/gitmind/attribution.h b/include/gitmind/attribution.h index 6daa71ed..8f2c172b 100644 --- a/include/gitmind/attribution.h +++ b/include/gitmind/attribution.h @@ -6,7 +6,8 @@ #include #include -#include "gitmind/edge_attributed.h" +/* Forward declaration to avoid header cycles */ +typedef struct gm_edge_attributed gm_edge_attributed_t; #ifdef __cplusplus extern "C" { diff --git a/core/include/gitmind/cbor/constants_cbor.h b/include/gitmind/cbor/constants_cbor.h similarity index 99% rename from core/include/gitmind/cbor/constants_cbor.h rename to include/gitmind/cbor/constants_cbor.h index 671bb8a4..f121c94c 100644 --- a/core/include/gitmind/cbor/constants_cbor.h +++ b/include/gitmind/cbor/constants_cbor.h @@ -69,3 +69,4 @@ (((x) << 24) & 0xFF000000)) #endif /* GITMIND_CONSTANTS_CBOR_H */ + diff --git a/include/gitmind/constants_internal.h b/include/gitmind/constants_internal.h index d05fd6b1..d1f7933f 100644 --- a/include/gitmind/constants_internal.h +++ b/include/gitmind/constants_internal.h @@ -21,7 +21,11 @@ 512 /* Git reference name buffer (must fit prefix + branch) */ #endif #ifndef SHA_HEX_SIZE -#define SHA_HEX_SIZE 41 /* SHA1 hex string size (40 + null) */ +# ifdef GIT_OID_HEXSZ +# define SHA_HEX_SIZE (GIT_OID_HEXSZ + 1) +# else +# define SHA_HEX_SIZE 41 /* Fallback: SHA1 hex string size (40 + null) */ +# endif #endif #define SHA_BYTES_SIZE 20 /* SHA1 size in bytes */ #define ZERO_SHA_STRING "0000000000000000000000000000000000000000" @@ -53,7 +57,7 @@ #define REFS_TAGS_PREFIX "refs/tags/" #define HEAD_REF "HEAD" #define GITMIND_NAMESPACE "refs/gitmind/" -#define GITMIND_JOURNAL_REF "refs/gitmind/journal" +#define GITMIND_EDGES_REF_PREFIX "refs/gitmind/edges/" #define GITMIND_CACHE_REF "refs/gitmind/cache" /* Exit Codes */ diff --git a/meson.build b/meson.build index 9b321fe0..17de18fc 100644 --- a/meson.build +++ b/meson.build @@ -1,25 +1,35 @@ project('git-mind', 'c', version: '0.6.0', - default_options: ['c_std=c23', 'warning_level=3', 'werror=true']) + default_options: ['c_std=c2x', 'warning_level=3', 'werror=true']) -# Optional feature toggles -option('enable_io', type: 'boolean', value: false, description: 'Enable IO public headers') -option('enable_time', type: 'boolean', value: false, description: 'Enable time public headers') -option('enable_util', type: 'boolean', value: false, description: 'Enable util public headers') -option('enable_utf8', type: 'boolean', value: true, description: 'Enable UTF-8 public headers') +## Optional feature toggles are declared in meson_options.txt # HARD E-BRAKE: refuse host builds unless in Docker or CI -in_docker = run_command('sh', '-lc', '[ "$GITMIND_DOCKER" = "1" ] && echo 1 || echo 0').stdout().strip() == '1' -in_ci = run_command('sh', '-lc', '[ -n "$GITHUB_ACTIONS" ] && echo 1 || echo 0').stdout().strip() == '1' -override = run_command('sh', '-lc', '[ "$GITMIND_ALLOW_HOST_BUILD" = "1" ] && echo 1 || echo 0').stdout().strip() == '1' -if not in_docker and not in_ci and not override - error('\n\n💥 Build refused: git-mind must be built in Docker.\n\n' + - 'Why: this project touches Git internals; host builds can corrupt your repo.\n' + - 'Do this instead:\n' + - ' make dev # open dev shell in CI container\n' + - ' make build-docker # build in container\n' + - ' make test-docker # test in container\n\n' + - 'If you REALLY know what you are doing, set GITMIND_ALLOW_HOST_BUILD=1.\n') +in_docker = run_command('sh', '-lc', '[ "$GITMIND_DOCKER" = "1" ] && echo 1 || echo 0', check: false).stdout().strip() == '1' +in_ci = run_command('sh', '-lc', '[ -n "$GITHUB_ACTIONS" ] && echo 1 || echo 0', check: false).stdout().strip() == '1' +env_override = run_command('sh', '-lc', '[ "$GITMIND_ALLOW_HOST_BUILD" = "1" ] && echo 1 || echo 0', check: false).stdout().strip() == '1' +force_local = get_option('force_local_builds') +if not in_docker and not in_ci and not env_override and not force_local + error('\n\n' + + '============================================================\n' + + '🚫 DO NOT BUILD OR RUN git-mind ON THE HOST\n' + + '============================================================\n' + + '\n' + + 'WHY THIS FAILS:\n' + + '- git-mind manipulates Git internals (refs/*, objects, config).\n' + + '- Building/tests on the host can corrupt THIS repository or others.\n' + + '\n' + + 'WHAT TO DO INSTEAD (Docker-only):\n' + + '- make ci-local # docs checks + build + tests in CI image\n' + + '- tools/ci/ci_local.sh # same as above, direct\n' + + '\n' + + 'IF YOU REALLY KNOW WHAT YOU\'RE DOING (at your own risk):\n' + + '- meson setup build -Dforce_local_builds=true\n' + + '- or: export GITMIND_ALLOW_HOST_BUILD=1 (discouraged)\n' + + '\n' + + 'Notes:\n' + + '- Overrides are your responsibility; we cannot protect your repo.\n' + + '- CI always runs inside a locked-down container.\n') endif # C23 compiler flags as specified @@ -113,10 +123,11 @@ src = files( 'core/src/cache/query.c', 'core/src/cache/tree_builder.c', 'core/src/cache/tree_size.c', + 'core/src/util/ref.c', ) # Include directories -inc = include_directories('core/include', 'include') +inc = include_directories('include', 'core/include') # Mark roaring headers as system includes to suppress third-party warnings roaring_inc_args = [] @@ -152,31 +163,31 @@ test_error = executable('test_error', 'core/tests/unit/test_error.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_id = executable('test_id', 'core/tests/unit/test_id.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_id_thread_safety = executable('test_id_thread_safety', 'core/tests/unit/test_id_thread_safety.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_string = executable('test_string', 'core/tests/unit/test_string.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_path = executable('test_path', 'core/tests/unit/test_path.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_sha256 = executable('test_sha256', 'core/tests/unit/test_sha256.c', @@ -196,63 +207,63 @@ test_utf8 = executable('test_utf8', 'core/tests/unit/test_utf8.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_crypto_backend = executable('test_crypto_backend', 'core/tests/unit/test_crypto_backend.c', include_directories : inc, link_with : [libgitmind, test_backend_lib], - dependencies : [libsodium_dep, thread_dep], + dependencies : [libsodium_dep, libgit2_dep, thread_dep], c_args : ['-DGITMIND_ENABLE_TEST_BACKEND']) test_ulid = executable('test_ulid', 'core/tests/unit/test_ulid.c', include_directories : inc, link_with : [libgitmind, test_backend_lib], - dependencies : [libsodium_dep, thread_dep], + dependencies : [libsodium_dep, libgit2_dep, thread_dep], c_args : ['-DGITMIND_ENABLE_TEST_BACKEND']) test_io = executable('test_io', 'core/tests/unit/test_io.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_time = executable('test_time', 'core/tests/unit/test_time.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_cbor = executable('test_cbor', 'core/tests/unit/test_cbor.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_edge = executable('test_edge', 'core/tests/unit/test_edge.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_edge_cbor_oid = executable('test_edge_cbor_oid', 'core/tests/unit/test_edge_cbor_oid.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_edge_attributed_cbor = executable('test_edge_attributed_cbor', 'core/tests/unit/test_edge_attributed_cbor.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_edge_equal_semantics = executable('test_edge_equal_semantics', 'core/tests/unit/test_edge_equal_semantics.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) test_cache_meta = executable('test_cache_meta', 'core/tests/unit/test_cache_meta.c', @@ -272,11 +283,41 @@ test_cache_query = executable('test_cache_query', 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, + link_with : libgitmind, + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) + test_journal_safety = executable('test_journal_safety', 'core/tests/unit/test_journal_safety.c', include_directories : inc, link_with : libgitmind, - dependencies : [libsodium_dep, thread_dep]) + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) + +test_cache_shard_distribution = executable('test_cache_shard_distribution', + 'core/tests/unit/test_cache_shard_distribution.c', + include_directories : inc, + link_with : libgitmind, + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) + +test_cache_tree_size = executable('test_cache_tree_size', + 'core/tests/unit/test_cache_tree_size.c', + include_directories : inc, + link_with : libgitmind, + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) + +test_edge_oid_fallback = executable('test_edge_oid_fallback', + 'core/tests/unit/test_edge_oid_fallback.c', + include_directories : inc, + link_with : libgitmind, + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) +test_ref_utils = executable('test_ref_utils', + 'core/tests/unit/test_ref_utils.c', + include_directories : inc, + link_with : libgitmind, + dependencies : [libsodium_dep, libgit2_dep, thread_dep]) + # Register tests test('error', test_error) @@ -299,7 +340,12 @@ test('edge_equal_semantics', test_edge_equal_semantics) test('cache_meta', test_cache_meta) test('journal_mixed_cbor', test_journal_mixed_cbor) test('cache_query', test_cache_query) +test('cache_branch_limits', test_cache_branch_limits) test('journal_safety', test_journal_safety) +test('cache_shard_distribution', test_cache_shard_distribution) +test('cache_tree_size', test_cache_tree_size) +test('edge_oid_fallback', test_edge_oid_fallback) +test('ref_utils', test_ref_utils) # Standalone header compilation checks (public headers) header_check_src = 'tools/header_compile.c' @@ -328,7 +374,7 @@ foreach hdr : public_headers header_check_src, include_directories : inc, dependencies : [libsodium_dep, libgit2_dep, thread_dep, roaring_dep], - c_args : ['-include', hdr], + c_args : ['-include', '../' + hdr], ) endforeach diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..e62db900 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,9 @@ +# Feature toggles +option('enable_io', type: 'boolean', value: false, description: 'Enable IO public headers') +option('enable_time', type: 'boolean', value: false, description: 'Enable time public headers') +option('enable_util', type: 'boolean', value: false, description: 'Enable util public headers') +option('enable_utf8', type: 'boolean', value: true, description: 'Enable UTF-8 public headers') + +# Safety override (dangerous): allow building on host instead of Docker/CI +option('force_local_builds', type: 'boolean', value: false, + description: 'Allow host builds (dangerous; use Docker by default)') diff --git a/scripts/update_progress.py b/scripts/update_progress.py new file mode 100644 index 00000000..be34f759 --- /dev/null +++ b/scripts/update_progress.py @@ -0,0 +1,271 @@ +#!/usr/bin/env python3 +""" +Update feature progress bars in docs/features/Features_Ledger.md and README.md. + +Progress is derived from per-feature percentages using KLoC as weights. Milestone +bars aggregate features by milestone, while the overall bar blends milestones via +configurable weights. +""" +from __future__ import annotations +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +LEDGER = ROOT / "docs" / "features" / "Features_Ledger.md" +README = ROOT / "README.md" + +GROUP_RE = re.compile(r"\n```text\n(.*?)\n```\n", re.S | re.I) +OVERALL_RE = re.compile(r"\n```text\n(.*?)\n```\n", re.S) +READ_ME_BLOCK_RE = re.compile(r"\n```text\n(.*?)\n```\n", re.S) +OVERALL_SECTION_RE = re.compile(r"\n```text\n(.*?)\n```\n", re.S) +MILESTONE_RE = { + key: re.compile(rf"\n```text\n(.*?)\n```\n", re.S) + for key in ("mvp", "alpha", "beta", "v1") +} +TASK_BLOCK_RE = { + key: re.compile(rf"> \n(.*?)\n> ", re.S) + for key in ("mvp", "alpha", "beta", "v1") +} + +MILESTONE_LABELS = { + "MVP": "mvp", + "Alpha": "alpha", + "Beta": "beta", + "v1.0.0": "v1", + "V1": "v1", +} + +MILESTONE_WEIGHTS = { + "mvp": 0.3, + "alpha": 0.3, + "beta": 0.2, + "v1": 0.2, +} + +def progress_bar(pct: float, width: int = 40) -> str: + pct = max(0.0, min(1.0, pct)) + filled = int(round(pct * width)) + filled = min(filled, width) + remainder = pct * width - filled + edge = 1 if filled < width and remainder > 0 else 0 + return "█" * filled + ("▓" if edge else "") + "░" * (width - filled - edge) + f" {int(round(pct * 100))}%" + +def compute_group_percent(md: str, anchor: int, entries: list[tuple[str, float, float]] | None = None) -> tuple[float, float, int]: + tail = md[anchor:] + # find the feature table header + m = re.search(r"^\|\s*Emoji\s*\|", tail, re.M) + if not m: + return 0.0, 0.0, 0 + i = m.start() + table = tail[i:].splitlines() + if len(table) < 3: + return 0.0, 0.0, 0 + header = table[0] + rows = table[2:] + header_cells = [c.strip() for c in header.strip().split('|')] + # locate column indices + def find_idx(name_sub: str) -> int: + for idx, c in enumerate(header_cells): + if name_sub.lower() in c.lower(): + return idx + return -1 + progress_idx = find_idx('Progress') + kloc_idx = find_idx('KLoC') + milestone_idx = find_idx('Milestone') + vals: list[float] = [] + weights: list[float] = [] + count = 0 + for line in rows: + if not line.startswith('|'): + break + cells = [c.strip() for c in line.strip().split('|')] + prog = None + # prefer indexed cell + if 0 <= progress_idx < len(cells): + c = cells[progress_idx] + if c.endswith('%') and c[:-1].strip().isdigit(): + prog = int(c[:-1].strip()) + else: + s2 = ''.join(ch for ch in c if ch.isdigit()) + if s2: + prog = int(s2) + # fallback scan + if prog is None: + for c in cells: + if c.endswith('%') and c[:-1].strip().isdigit(): + prog = int(c[:-1].strip()) + break + if prog is None: + continue + # weight from KLoC (default small weight so features without data still count) + w = 0.1 + if 0 <= kloc_idx < len(cells): + try: + raw = cells[kloc_idx].replace(',', '') + match = re.search(r"-?\d+(?:\.\d+)?", raw) + if match: + kloc = float(match.group(0)) + if kloc > 0: + w = kloc + except Exception: + w = 0.1 + vals.append(prog) + weights.append(w) + count += 1 + if entries is not None: + milestone = "Unassigned" + if 0 <= milestone_idx < len(cells): + milestone = cells[milestone_idx] or "Unassigned" + entries.append((milestone.strip(), prog / 100.0, w)) + if not vals: + return 0.0, 0.0, 0 + wsum = sum(weights) + if wsum <= 0: + pct = sum(vals) / len(vals) / 100.0 + else: + pct = (sum(p * w for p, w in zip(vals, weights)) / wsum) / 100.0 + return pct, wsum, count + +def extract_tasks(md: str) -> dict[str, list[str]]: + tasks: dict[str, list[str]] = {k: [] for k in MILESTONE_RE.keys()} + lower = md.lower() + if "## tasklist" not in lower: + return tasks + start = lower.index("## tasklist") + segment = md[start:] + for line in segment.splitlines(): + stripped = line.strip() + if stripped.startswith("- [ ]"): + tag = None + rest = stripped[len("- [ ]"):].strip() + if rest.startswith("[") and "]" in rest: + tag_name = rest[1:rest.index("]")] + key = MILESTONE_LABELS.get(tag_name, None) + if key: + tag = key + rest = rest[rest.index("]") + 1:].strip() + entry = f"- [ ] {rest}" if rest else "- [ ]" + else: + entry = stripped + else: + entry = stripped + if not tag: + tag = "mvp" + tasks.setdefault(tag, []).append(entry if tag in tasks else stripped) + return tasks + + +def update_ledger() -> tuple[float | None, dict[str, float], dict[str, list[str]]]: + text = LEDGER.read_text(encoding='utf-8') + groups = [] + new = text + feature_entries: list[tuple[str, float, float]] = [] + for m in GROUP_RE.finditer(text): + start = m.start() + name = m.group(1) + pct, weight, count = compute_group_percent(text, m.end(), feature_entries) + bar = progress_bar(pct) + feature_tag = f"features={count}" if count else "features=0" + block = ( + f"\n" \ + "```text\n" \ + f"{bar}\n" \ + "------------|-------------|------------|\n" \ + " MVP Alpha v1.0.0 \n" \ + f"{feature_tag}\n" \ + "```\n" \ + f"" + ) + new = new.replace(m.group(0), block) + groups.append((pct, weight if weight > 0 else float(count or 0))) + milestone_totals: dict[str, tuple[float, float]] = { + key: (0.0, 0.0) for key in MILESTONE_RE.keys() + } + for raw_label, pct, weight in feature_entries: + key = MILESTONE_LABELS.get(raw_label or "", None) + if not key: + continue + total_pct, total_weight = milestone_totals[key] + milestone_totals[key] = (total_pct + pct * weight, total_weight + weight) + milestone_progress: dict[str, float] = {} + for key, (total, weight) in milestone_totals.items(): + if weight > 0: + milestone_progress[key] = total / weight + else: + milestone_progress[key] = 0.0 + overall = None + if milestone_progress: + total_weight = sum(MILESTONE_WEIGHTS.values()) + if total_weight > 0: + overall = sum(milestone_progress.get(k, 0.0) * w for k, w in MILESTONE_WEIGHTS.items()) / total_weight + over_m = OVERALL_SECTION_RE.search(new) + if over_m: + legend = " | ".join( + f"{name.upper()} {int(round(milestone_progress.get(key, 0.0) * 100))}%" + for name, key in (("MVP", "mvp"), ("Alpha", "alpha"), ("Beta", "beta"), ("v1.0.0", "v1")) + ) + over_block = ( + "\n" + "```text\n" + f"{progress_bar(overall or 0.0)}\n" + f"{legend}\n" + "```\n" + "" + ) + new = new.replace(over_m.group(0), over_block) + for label, regex in MILESTONE_RE.items(): + match = regex.search(new) + if not match: + continue + pct = milestone_progress.get(label, 0.0) + block = ( + f"\n" + "```text\n" + f"{progress_bar(pct)}\n" + "```\n" + f"" + ) + new = new.replace(match.group(0), block) + tasks = extract_tasks(new) + for label, regex in TASK_BLOCK_RE.items(): + match = regex.search(new) + if not match: + continue + items = tasks.get(label, []) if tasks else [] + if not items: + body = "> _All tracked tasks complete_" + else: + body = "\n".join(f"> {item}" for item in items) + replacement = f"> \n{body}\n> " + new = new.replace(match.group(0), replacement) + if new != text: + LEDGER.write_text(new, encoding='utf-8') + return overall, milestone_progress, tasks + +def update_readme(overall: float | None = None): + if not README.exists(): + return + text = README.read_text(encoding='utf-8') + if "## 📊 Status" not in text: + return + if not READ_ME_BLOCK_RE.search(text): + parts = text.split("## 📊 Status", 1) + progress = "\n\n```text\nFeature progress to be updated via scripts/update_progress.py\n```\n\n\n" + text = parts[0] + "## 📊 Status\n\n" + progress + parts[1] + if overall is not None: + block = f"\n```text\n{progress_bar(overall)}\n```\n\n" + # Remove all existing blocks, then insert one + text = READ_ME_BLOCK_RE.sub("", text) + # Insert just after the Status header + parts = text.split("## 📊 Status", 1) + if len(parts) == 2: + text = parts[0] + "## 📊 Status\n\n" + block + "\n" + parts[1] + text = re.sub(r"\n{3,}", "\n\n", text) + README.write_text(text, encoding='utf-8') + +def main(): + overall, _, _ = update_ledger() + update_readme(overall) + +if __name__ == '__main__': + main() diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index bf708003..00000000 --- a/src/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# This directory should NEVER have build artifacts! -*.o -*.a -*.so -*.exe -*.out - -# If you see these files, you built outside Docker! -# Run 'make clean' from the root directory \ No newline at end of file diff --git a/tools/agents/curate_recent.py b/tools/agents/curate_recent.py new file mode 100644 index 00000000..a84f511e --- /dev/null +++ b/tools/agents/curate_recent.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +""" +Curate AGENTS.md recent developments and archive older activity. + +Behavior: +- Extract date-stamped entries from all "## Agent Activity Log" sections. +- Keep only the most recent date's entries in a "## Recent Developments" section + near the top (as a machine-readable YAML block + bullets). +- Archive older date sections into docs/activity/YYYY-MM.md, grouped by month. +- Replace the original "## Agent Activity Log" content with a short pointer to archives. + +Idempotent: safe to run multiple times; only rewrites when changes are needed. +""" +from __future__ import annotations + +import re +import sys +from pathlib import Path +from datetime import datetime, timezone + +ROOT = Path(__file__).resolve().parents[2] +AGENTS = ROOT / "AGENTS.md" +ARCHIVE_DIR = ROOT / "docs" / "activity" + +HEADER_RECENT = "## Recent Developments" +HEADER_ACTIVITY = "## Agent Activity Log" +HEADER_TLDR = "## Agent TL;DR (read me first)" +HEADER_BOOT = "## Boot Checklist" + +DATE_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})\s*$") + + +def split_sections(lines: list[str], header: str) -> list[tuple[int, int]]: + """Return list of (start, end) indexes for sections matching the header.""" + indices = [] + i = 0 + n = len(lines) + while i < n: + if lines[i].strip() == header: + # section from i to next top-level header or end + j = i + 1 + while j < n and not (lines[j].startswith("## ") and lines[j].strip() != header): + j += 1 + indices.append((i, j)) + i = j + else: + i += 1 + return indices + + +def extract_date_blocks(section_lines: list[str]) -> list[tuple[str, list[str]]]: + """Extract (date, block_lines) from a section body containing date headings.""" + blocks = [] + i = 0 + n = len(section_lines) + while i < n: + m = DATE_RE.match(section_lines[i].strip()) if section_lines[i].strip() else None + if m: + date = m.group(1) + j = i + 1 + # until next date or EOF or next top-level header (shouldn't be here) + while j < n: + line = section_lines[j] + if DATE_RE.match(line.strip()): + break + if line.startswith("## "): + break + j += 1 + blocks.append((date, section_lines[i:j])) + i = j + else: + i += 1 + return blocks + + +def first_bullets(block_lines: list[str], limit: int = 6) -> list[str]: + out = [] + for line in block_lines: + s = line.strip() + if s.startswith("-"): + out.append(s[1:].strip()) + if len(out) >= limit: + break + return out + + +def _render_recent_yaml(date: str, bullets: list[str]) -> list[str]: + import textwrap + out = ["```yaml\n", "recent_developments:\n"] + for b in bullets: + out.append(f" - date: {date}\n") + out.append(" summary: >\n") + for line in textwrap.wrap(b, width=100): + out.append(" " + line + "\n") + out.append("```\n") + return out + + +def _section_range(lines: list[str], header: str): + n = len(lines) + i = 0 + start = end = None + while i < n: + if lines[i].strip() == header: + start = i + j = i + 1 + while j < n and not (lines[j].startswith("## ") and lines[j].strip() != header): + j += 1 + end = j + break + i += 1 + return (start, end) if start is not None else (None, None) + + +def ensure_recent_developments(lines: list[str], date: str, bullets: list[str]) -> list[str]: + rendered = [HEADER_RECENT + "\n", "\n"] + _render_recent_yaml(date, bullets) + ["\n"] + + start, end = _section_range(lines, HEADER_RECENT) + if start is not None: + return lines[:start] + rendered + lines[end:] + + # Prefer insertion after Boot Checklist, else after TLDR, else near top + _, boot_end = _section_range(lines, HEADER_BOOT) + if boot_end is not None: + insert_at = boot_end + return lines[:insert_at] + ["\n"] + rendered + lines[insert_at:] + + _, tldr_end = _section_range(lines, HEADER_TLDR) + if tldr_end is not None: + insert_at = tldr_end + return lines[:insert_at] + ["\n"] + rendered + lines[insert_at:] + + insert_at = 1 if len(lines) > 1 else 0 + return lines[:insert_at] + ["\n"] + rendered + lines[insert_at:] + + +def archive_blocks(blocks: list[tuple[str, list[str]]]): + ARCHIVE_DIR.mkdir(parents=True, exist_ok=True) + for date, content in blocks: + dt = datetime.strptime(date, "%Y-%m-%d") + fname = ARCHIVE_DIR / f"{dt.year}-{dt.month:02d}.md" + if not fname.exists(): + fname.write_text( + "---\n" + f"title: Activity {dt.year}-{dt.month:02d}\n" + "description: Archived agent activity logs.\n" + "audience: [contributors]\n" + "domain: [quality]\n" + "tags: [activity]\n" + "status: archive\n" + f"last_updated: {datetime.now(timezone.utc).date()}\n" + "---\n\n", + encoding="utf-8", + ) + with fname.open("a", encoding="utf-8") as f: + f.write(f"## {date}\n\n") + f.writelines(content) + if not content[-1].endswith("\n"): + f.write("\n") + f.write("\n") + + +def curate(): + if not AGENTS.exists(): + return 0 + orig = AGENTS.read_text(encoding="utf-8") + lines = orig.splitlines(keepends=True) + + # Find all Agent Activity Log sections and collect date blocks + sections = split_sections(lines, HEADER_ACTIVITY) + if not sections: + return 0 + + all_blocks: list[tuple[str, list[str], tuple[int, int]]] = [] + for (s, e) in sections: + body = lines[s + 1:e] + blocks = extract_date_blocks(body) + for date, content in blocks: + all_blocks.append((date, content, (s, e))) + + if not all_blocks: + return 0 + + # Determine most recent date + all_blocks.sort(key=lambda x: x[0]) + latest_date = all_blocks[-1][0] + + latest_blocks = [(d, c) for (d, c, _) in all_blocks if d == latest_date] + older_blocks = [(d, c) for (d, c, _) in all_blocks if d != latest_date] + + # Build bullets from first latest block + bullets = first_bullets(latest_blocks[0][1]) if latest_blocks else [] + new_lines = ensure_recent_developments(lines, latest_date, bullets) + + # Replace activity sections with pointer + pointer = [HEADER_ACTIVITY + "\n", "\n", "See archives under `docs/activity/` for older logs.\n", "\n"] + # Remove existing activity sections and insert pointer once (after first occurrence) + secs = split_sections(new_lines, HEADER_ACTIVITY) + if secs: + s0, e0 = secs[0] + new_lines = new_lines[:s0] + pointer + new_lines[e0:] + # Remove any additional activity sections + while True: + more = split_sections(new_lines, HEADER_ACTIVITY) + if len(more) <= 1: + break + s, e = more[1] + new_lines = new_lines[:s] + new_lines[e:] + + # Archive older blocks + if older_blocks: + archive_blocks(older_blocks) + + new_text = "".join(new_lines) + if new_text != orig: + AGENTS.write_text(new_text, encoding="utf-8") + # Signal to pre-commit that files changed; it will re-run + print("Curated Recent Developments and archived older activity logs.") + return 1 + return 0 + + +def main(): + sys.exit(curate()) + + +if __name__ == "__main__": + main() diff --git a/tools/changelog/add_entry.py b/tools/changelog/add_entry.py new file mode 100644 index 00000000..41975b07 --- /dev/null +++ b/tools/changelog/add_entry.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +import argparse +import datetime as dt +import os +import subprocess +import sys + +CHANGELOG = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'CHANGELOG.md') + +def git_short_sha(): + try: + out = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], stderr=subprocess.DEVNULL) + return out.decode().strip() + except Exception: + return 'unknown' + +def ensure_date_section(lines, date_str): + header = f"## {date_str}\n" + if any(l.strip() == header.strip() for l in lines): + return lines + # Insert date header under "## Daily Dev Log" section or append at end + new_lines = [] + inserted = False + in_daily = False + for i, line in enumerate(lines): + new_lines.append(line) + if line.startswith('## Daily Dev Log'): + in_daily = True + continue + if in_daily and not inserted: + # next blank line is a good insertion point + if line.strip() == '' and i + 1 < len(lines): + new_lines.append(header) + new_lines.append('\n') + inserted = True + in_daily = False + if not inserted: + if not new_lines or new_lines[-1].strip() != '': + new_lines.append('\n') + new_lines.append(header) + new_lines.append('\n') + return new_lines + +def main(): + parser = argparse.ArgumentParser(description='Append a daily dev log entry to CHANGELOG.md') + parser.add_argument('-m', '--message', required=True, help='One-liner description') + parser.add_argument('-n', '--note', default=None, help='Optional note block to add') + args = parser.parse_args() + + now = dt.datetime.now() + date_str = now.strftime('%Y-%m-%d') + time_str = now.strftime('%H:%M') + sha = git_short_sha() + + if not os.path.exists(CHANGELOG): + print(f"CHANGELOG not found at {CHANGELOG}", file=sys.stderr) + return 1 + + with open(CHANGELOG, 'r', encoding='utf-8') as f: + lines = f.readlines() + + lines = ensure_date_section(lines, date_str) + + entry = f"- @ {time_str} – `{sha}` {args.message}\n" + lines.append(entry) + if args.note: + lines.append('\n') + lines.append('> [!note]\n') + lines.append(f"> {args.note}\n") + lines.append('\n') + + with open(CHANGELOG, 'w', encoding='utf-8') as f: + f.writelines(lines) + return 0 + +if __name__ == '__main__': + sys.exit(main()) + diff --git a/tools/ci/ci_local.sh b/tools/ci/ci_local.sh new file mode 100644 index 00000000..7d8ccfd5 --- /dev/null +++ b/tools/ci/ci_local.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Local CI rig mirroring the C-Core Gate workflow +# - Markdown lint + docs checks +# - Optionally build and tidy on host (if requested) +# - Dockerized build + unit tests + E2E using the CI image + +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +cd "$ROOT_DIR" + +echo "==> Docs checks (frontmatter + links + TOC)" +python3 tools/docs/check_frontmatter.py +python3 tools/docs/check_docs.py --mode link +python3 tools/docs/check_docs.py --mode toc + +IMAGE="${GITMIND_CI_IMAGE:-gitmind/ci:clang-20}" +echo "==> Using CI image: $IMAGE" + +if ! command -v docker >/dev/null 2>&1; then + echo "⚠️ Docker not found. Falling back to host build (override guard)." + export GITMIND_ALLOW_HOST_BUILD=1 + CC=${CC:-clang} + echo "==> Host build with $CC" + rm -rf build-local + CC=$CC meson setup build-local + ninja -C build-local + ninja -C build-local test + exit 0 +fi + +echo "==> Pulling CI image (best-effort)" +docker pull "$IMAGE" || true + +echo "==> Containerized build + tests + E2E" +docker run --rm --label com.gitmind.project=git-mind \ + -u "$(id -u):$(id -g)" \ + -v "$PWD":/workspace -w /workspace \ + -e GITMIND_DOCKER=1 -e GITMIND_SAFETY=off "$IMAGE" bash -lc ' + set -euo pipefail + BUILD_DIR=/workspace/build-local + if [ -d "$BUILD_DIR" ]; then + CC=clang meson setup --reconfigure "$BUILD_DIR" + else + CC=clang meson setup "$BUILD_DIR" + fi + ninja -C "$BUILD_DIR" test + + echo "==> clang-tidy (diff-guard style)" + cd /workspace + cp "$BUILD_DIR/compile_commands.json" . || true + if command -v clang-tidy >/dev/null 2>&1; then + clang-tidy -quiet -p . --config-file=quality/.clang-tidy \ + $(git ls-files "core/**/*.c" "core/**/*.h" | grep -v -E "(test_|_test\\.|/tests/|core/src/hooks/)") \ + | tee clang-tidy-report-full.txt || true + grep -E "^/.*core/|^core/" clang-tidy-report-full.txt > clang-tidy-report.txt || true + echo "clang-tidy finished (report: clang-tidy-report.txt)" + else + echo "clang-tidy not found in image; skipping" + fi + ' + +echo "✅ Local CI completed" diff --git a/tools/docs/check_frontmatter.py b/tools/docs/check_frontmatter.py new file mode 100644 index 00000000..43ab584e --- /dev/null +++ b/tools/docs/check_frontmatter.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +""" +Check that selected documentation Markdown files contain a YAML frontmatter block +with required keys. Designed to be simple and fast, matching our repo layout. + +Usage: + python3 tools/docs/check_frontmatter.py [--base docs] + +Exit codes: + 0 = all good + 1 = problems found +""" + +from __future__ import annotations +import argparse +import pathlib +import sys +from typing import List, Tuple + + +REQUIRED_KEYS = [ + "title", + "description", + "audience", + "domain", + "tags", + "status", +] + +# Directories to include for enforcement +INCLUDE_DIRS = { + "docs/architecture": True, + "docs/cli": True, + "docs/operations": True, + "docs/quality": True, + "docs/testing": True, + "docs/PRDs": True, + "docs/adr": True, + "docs/requirements": True, + "docs/deployment": True, + "docs/charter": True, + "docs/api": True, + "docs/risk": True, + "docs/specs": True, +} + +# Specific files to always include (live at docs root) +ALWAYS_INCLUDE = { + "docs/README.md", + "docs/install.md", + "docs/tutorial.md", + "docs/philosophy.md", + "docs/TECHNICAL.md", + "docs/roadmap.md", + "docs/operations/Environment_Variables.md", +} + +# Subtrees to exclude (journals, brainstorming, reviews, etc.) +EXCLUDE_DIR_PREFIXES = ( + # None: enforce everywhere we include +) + + +def should_check(path: pathlib.Path) -> bool: + p = str(path).replace("\\", "/") + if not p.endswith(".md"): + return False + for pref in EXCLUDE_DIR_PREFIXES: + if p.startswith(pref): + return False + # Enforce only for included dirs + # Always include select root docs + if p in ALWAYS_INCLUDE: + return True + for inc in INCLUDE_DIRS.keys(): + if p.startswith(inc + "/") or p == inc + ".md": + return True + return False + + +def parse_frontmatter(text: str) -> Tuple[bool, List[str]]: + """Return (ok, missing_keys). Accepts HTML comments or blank lines before frontmatter. + Frontmatter format: + ---\n + key: value\n + ...\n + --- + """ + lines = text.splitlines() + i = 0 + # Skip leading blank and HTML comment lines + while i < len(lines) and (not lines[i].strip() or lines[i].lstrip().startswith("