Skip to content

feat: upgrade git-warp to v14.16.2#314

Open
flyingrobots wants to merge 5 commits intomainfrom
feat/git-warp-upgrade-audit
Open

feat: upgrade git-warp to v14.16.2#314
flyingrobots wants to merge 5 commits intomainfrom
feat/git-warp-upgrade-audit

Conversation

@flyingrobots
Copy link
Owner

@flyingrobots flyingrobots commented Mar 25, 2026

PR Title

feat: upgrade git-warp to v14.16.2

Summary

  • upgrade @git-stunts/git-warp from 11.5.0 to 14.16.2
  • add a property-bag compatibility layer and apply it across Git Mind's git-warp boundary
  • harden observer, epoch, review, export, merge, and content handling for the v14 object-shaped property model
  • align contributor/docs guidance with the published-package dependency model and Node 22 runtime
  • extend regression coverage for compatibility edge cases and normalize CLI contract test timeouts

Problem Statement

  • Git Mind was three major versions behind on @git-stunts/git-warp and was still coded against the older Map-shaped getNodeProps() behavior.
  • That left the repo planning new work on top of a stale substrate and made the upgrade risk hard to reason about.
  • This PR moves the runtime to the current v14 line, documents the boundary, and locks in the compatibility expectations with tests before Hill 1 implementation work continues.

ADR Compliance (Required)

Relevant ADR(s)

  • ADR-0002 (Worktree Independence and Materialization Architecture)
  • ADR-0003 (Graph-Native Content, Deterministic Materialization, and Workspace Bridge)
  • None

Compliance Declaration

  • This PR is fully compliant with all checked ADRs.
  • This PR intentionally deviates from one or more checked ADRs (complete Exception Request below).

Exception Request (Required if deviating)

  • ADR clause(s) deviated from: None
  • Why deviation is necessary now: N/A
  • Risk introduced by deviation: N/A
  • Mitigation and rollback plan: N/A
  • Follow-up ADR/issue to reconcile architecture debt: N/A

Architecture Laws Checklist (Hard Gates)

Canonical Truth & Context

  • Graph remains canonical truth (no dual truth with generated files).
  • No hidden worktree coupling introduced in core/domain/materialization paths.
  • Context-sensitive behavior is explicit (--at, --observer, --trust) or deterministically defaulted.
  • Resolved context is surfaced in output metadata where applicable.

Determinism & Provenance

  • Pure query/materialization paths remain deterministic for identical inputs.
  • Mutations/materializations include provenance receipts/envelopes where required.
  • Cache keys (if used) are derived only from semantic inputs + pinned versions.

Artifact Hygiene

  • No forbidden generated artifact paths are tracked.
  • Any generated artifacts intentionally tracked are in allowlisted publish paths only.
  • Pre-commit/CI policy checks updated or confirmed valid.

Contracts & Compatibility

  • Machine-facing outputs are schema-versioned.
  • Breaking contract changes include version bump + migration notes.
  • Backward compatibility impact is documented below.

Extension/Effects Safety (if applicable)

  • Extension behavior does not bypass capability restrictions.
  • Effectful operations use explicit plan/apply semantics and emit receipts.
  • Timeouts/resource bounds are defined for new script/effect paths.

Scope Control

  • PR is single-purpose/cohesive (no unrelated refactors).
  • Any non-essential refactor is split into separate PR(s) or explicitly justified.

Backward Compatibility

  • CLI/API contract changes: No intentional CLI schema change. Existing commands now tolerate both Map-shaped and object-shaped property bags at the git-warp boundary.
  • Data model/storage changes: No Git Mind graph schema change. Dependency runtime moved from git-warp 11 to 14.
  • Migration required?: No user migration step beyond reinstalling dependencies and using Node 22.
  • User-facing behavior changes: Minimum runtime is now Node.js 22+. Observer resolution and malformed epoch/review records now fail or skip defensively instead of flowing through undefined values.

Test Plan (Required)

Unit

  • Added/updated tests for changed logic
  • Commands:
npm test -- test/prop-bag.test.js test/resolve-context.test.js test/review.test.js test/epoch.test.js test/export.test.js test/contracts.integration.test.js

Integration

  • Added/updated integration tests
  • Commands:
npm test -- test/contracts.integration.test.js

Determinism

  • Determinism assertions included for relevant paths
  • Method: existing diff/materialization/integration suites exercised against the upgraded dependency boundary
  • Commands:
npm test -- test/diff.test.js test/graph.test.js test/import.test.js test/merge.test.js

Contract/Schema

  • Schema validation updated/passing
  • Commands:
npm test -- test/contracts.integration.test.js test/contracts.test.js

Policy Gates

  • Mechanical architecture gates pass
  • Commands:
npm run lint
npm test

Security / Trust Impact

  • Threat surface changed?: Minimal; dependency upgrade only. Defensive validation now rejects malformed observer/epoch/review inputs earlier.
  • Trust policy impact: None.
  • Provenance/audit impact: Review and epoch history now skip malformed records instead of emitting partially undefined data.
  • New failure modes introduced: Observer configs missing match now fail fast with an explicit error.

Performance Impact

  • Hot path affected?: Slightly; property access now goes through compatibility helpers.
  • Expected impact (latency/memory/io): Negligible. One Buffer copy was removed for Buffer-based content reads.
  • Benchmarks or profiling evidence: Full suite and CLI contract canaries remained green after the upgrade; CLI timeout budget was normalized to avoid false negatives.

Observability / Debuggability

  • Errors are actionable and include context.
  • Logs/diagnostics added or updated where needed.
  • git mind status / diagnostics updated if writeback/eventing behavior changed.

Operational Notes

  • Feature flag (if any): None.
  • Rollback strategy: Revert this PR and reinstall dependencies to return to git-warp 11.5.0.
  • Operational caveats: Contributors now need Node.js 22+ for local development on this branch line.

Linked Issues / Milestones


Reviewer Quick Verdict Block (for maintainers)

MUST (Hard Gates)

  • PASS

SHOULD (Quality)

  • PASS

Verdict

  • APPROVE

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 25, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced compatibility layer for data structure handling across different library versions.
  • Bug Fixes

    • Improved validation and error handling for malformed data.
    • Strengthened edge-case coverage for property management and observer configuration.
  • Documentation

    • Updated minimum Node.js requirement to version 22.
    • Added git-warp upgrade audit design documentation.
    • Refreshed technical stack and contribution guides.
  • Tests

    • Added comprehensive test coverage for validation boundaries and data format conversion.

Walkthrough

Bumps @git-stunts/git-warp to ^14.16.2 and Node.js engine to >=22; adds src/prop-bag.js to normalize Map/object property bags; updates multiple source files to use prop-bag helpers; adds tests and design doc for a git-warp upgrade audit; adjusts docs and CI test timeouts.

Changes

Cohort / File(s) Summary
Dependency & Docs
package.json, AGENTS.md, CLAUDE.md, CONTRIBUTING.md, GUIDE.md
Upgrade @git-stunts/git-warp to ^14.16.2; raise Node.js minimum to >=22; replace local-path dependency guidance with published-package instructions.
Roadmap & Design
ROADMAP.md, TECH-PLAN.md, docs/README.md, docs/design/git-warp-upgrade-audit.md
Add git-warp upgrade-audit design doc and roadmap Hill 1 prerequisite; remove hardcoded git-warp version in TECH-PLAN; add docs index entry.
Prop-bag Utility
src/prop-bag.js, test/prop-bag.test.js
New compatibility module exporting getProp, getPropEntries, getPropSize, toPropObject with comprehensive unit tests for Map/object/null shapes.
Core Source Updates
src/cli/commands.js, src/content.js, src/epoch.js, src/export.js, src/merge.js, src/nodes.js, src/review.js, src/suggest.js, src/views.js
Replace direct Map access and size/entry handling with prop-bag helpers; add validations (observer match, epoch/review record shape) and small API-surface JSDoc updates.
Tests & Timeouts
test/contracts.integration.test.js, test/diff.test.js, test/graph.test.js, test/import.test.js, test/merge.test.js, test/epoch.test.js, test/export.test.js, test/resolve-context.test.js, test/review.test.js
Add/adjust tests to use getProp, add prop-bag coverage, add per-test timeout constants and wrapper, and add validation/negative-path tests for epoch/review/resolveContext behaviors.
Changelog
CHANGELOG.md
Document compatibility hardening for git-warp v14 and CLI integration timeout harmonization.

Sequence Diagram(s)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

Poem

A Map became an object; the bridge was made,
prop-bag stitches seams where formats frayed,
docs updated, tests tightened, Node raised high,
fourteen’s the target — compatibility nigh. 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary change: upgrading git-warp from v11.5.0 to v14.16.2.
Description check ✅ Passed The description comprehensively covers all required template sections, including problem statement, ADR compliance, architecture checklist, scope, backward compatibility, test plan, security impact, and operational notes with supporting evidence.
Linked Issues check ✅ Passed The PR fully satisfies all objectives from #312: dependency boundary inventoried, upgrade target justified, tests added/strengthened, runtime/contracts/tests green, docs cleaned up, acceptance criteria met.
Out of Scope Changes check ✅ Passed All changes cohere to the upgrade scope: new prop-bag compatibility layer, hardened boundary handling, documentation alignment, test expansion, and dependency/engine updates. No unrelated refactoring introduced.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/git-warp-upgrade-audit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3964fb8914

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

src/prop-bag.js Outdated
Comment on lines +50 to +51
if (typeof props.size === 'number') {
return props.size;

Choose a reason for hiding this comment

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

P2 Badge Detect Map bags without reading arbitrary size field

getPropSize currently treats any property bag with a numeric size field as Map-like. With the new object-shaped getNodeProps() payloads, nodes can legitimately have a numeric size property (for example from YAML import), so an object like { size: 0, status: 'todo' } is misclassified as empty. Because exportGraph and renderView both gate property handling on getPropSize(propsMap) > 0, those nodes lose all exported/view properties in this case. Please detect Map semantics explicitly (for example via instanceof Map or method checks) before trusting props.size.

Useful? React with 👍 / 👎.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/export.js (1)

88-89: ⚠️ Potential issue | 🟡 Minor

Apply getPropEntries abstraction to edge properties, not just node properties.

Edge properties at lines 88–89 use raw Object.entries(edge.props), while node properties (line 64) use the getPropEntries() abstraction. The abstraction exists explicitly because git-warp v14+ has a documented breaking change: older releases returned Maps from getNodeProps(), newer ones return plain objects. If edge.props undergoes a similar evolution and returns a Map in a future git-warp version, the current code will silently fail.

For consistency and robustness, wrap the edge properties iteration with the same abstraction:

Suggested fix pattern
- for (const [key, value] of Object.entries(edge.props)) {
+ for (const [key, value] of getPropEntries(edge.props)) {

Same pattern applies to src/merge.js:108.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/export.js` around lines 88 - 89, The iteration over edge properties uses
Object.entries(edge.props) which bypasses the getPropEntries abstraction and may
break if edge.props can be a Map; update the loop in export.js (the block that
checks "if (edge.props) { for (const [key, value] of
Object.entries(edge.props))") to use getPropEntries(edge.props) instead,
mirroring how node properties are handled elsewhere, and apply the same
replacement in merge.js at the loop around the code referenced on line 108 so
both edge property iterations consistently use getPropEntries.
src/merge.js (1)

107-113: ⚠️ Potential issue | 🟡 Minor

Normalize edge property iteration through getPropEntries() for consistency.

Edge properties on lines 108 and 89 (export.js) use raw Object.entries(edge.props), while node properties use getPropEntries(), which safely handles both Map and object prop formats. Although edge.props currently returns plain objects, git-warp's API contract shows edge props as Maps (matching node props), creating inconsistency that could break if the data structure changes.

Either document that edge props are contractually always plain objects, or apply the same defensive pattern: replace Object.entries(edge.props) with getPropEntries(edge.props) in merge.js (line 108) and export.js (line 89) to match node property handling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/merge.js` around lines 107 - 113, The loop over edge.props uses
Object.entries(edge.props) which is inconsistent with node prop handling;
replace Object.entries(edge.props) with getPropEntries(edge.props) in merge.js
(the block that checks MERGE_EDGE_PROPS and calls patch.setEdgeProperty) and the
similar spot in export.js to safely support Map or object prop formats; also
ensure getPropEntries is imported/available in those modules and keep the
existing checks for MERGE_EDGE_PROPS and value !== undefined/null before calling
patch.setEdgeProperty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@GUIDE.md`:
- Line 67: GUIDE.md currently states git-mind "depends on the published
`@git-stunts/git-warp` package" which contradicts AGENTS.md and CLAUDE.md that
require "git-stunts/git-warp (local path)" and also mandates
"git-stunts/plumbing" as a direct dependency; pick one model and make
consistent: either revert GUIDE.md to state the local-path requirement (update
the `git-stunts/git-warp` phrasing) and ensure CLAUDE.md/AGENTS.md remain
accurate, or update AGENTS.md and CLAUDE.md to reflect the published
`@git-stunts/git-warp` model; finally, ensure package.json lists the chosen
artifacts correctly (add or change entries for "git-stunts/git-warp" and
"git-stunts/plumbing" in dependencies or local path entries) so all three
documents (GUIDE.md, AGENTS.md, CLAUDE.md) and package.json are consistent.

In `@package.json`:
- Line 29: Replace the semver entry for "@git-stunts/git-warp" in package.json
with a local file: dependency that points at your local git-stunts monorepo
build (i.e. the built/git-warp package in your monorepo) so the project uses the
local CRDT engine instead of an npm version; update the lockfile by running your
package manager install afterwards and verify the package resolves to the local
path (check package.json's "@git-stunts/git-warp" value and the resolved entry
in package-lock.json/yarn.lock).

In `@src/cli/commands.js`:
- Around line 94-99: The code sets config.match = getProp(propsMap, 'match')
without checking for undefined, which can cause graph.observer(observer, config)
to fail; update the logic around getProp, propsMap and graph.observer to
validate that match is present (e.g., call getProp(propsMap, 'match'), check if
the returned value is undefined or empty, and if so throw or return a clear
error message before calling graph.observer), leaving the existing guards for
optional expose/redact in place; reference getProp, propsMap, config and
graph.observer when making the change so the required match property is
validated and the code fails fast with a descriptive error instead of passing
undefined into graph.observer.

In `@src/content.js`:
- Around line 21-30: The decodeContent function currently always calls
Buffer.from(content) which copies when content is already a Buffer; update
decodeContent to first handle Buffer inputs by returning
content.toString('utf-8') directly, keep the existing branch for string inputs,
and only call Buffer.from(content).toString('utf-8') for Uint8Array/other binary
types; reference the decodeContent function to locate and change the code
accordingly.

In `@src/epoch.js`:
- Around line 73-77: The EpochInfo factory currently returns properties from
getProp that may be undefined; update the function that builds the EpochInfo
(the return containing tick, fullSha, recordedAt) to validate required fields
(at minimum tick, and fullSha if used downstream) and fail fast: if any required
property is missing, either throw a descriptive Error or return null/undefined
so callers like getEpochForRef can handle absence before calling
graph.materialize({ ceiling }); reference getProp, the returned EpochInfo, and
callers like getEpochForRef/graph.materialize when adding this defensive check.

In `@src/prop-bag.js`:
- Around line 20-26: getProp currently duck-types by checking for a .get method
which misclassifies objects with custom get functions; change the detection to
explicitly check for Map instances (use "props instanceof Map") in the getProp
function so Map-like behavior is only used for real Map objects, and fall back
to own-property lookup via Object.prototype.hasOwnProperty.call(props, key)
otherwise; update the branch that now returns props.get(key) to only run when
props is an instance of Map.
- Around line 34-40: The getPropEntries helper currently duck-types on
props.entries(), which is fragile; change the branch to check for a Map instance
instead: in the getPropEntries function, replace the typeof props.entries ===
'function' check with props instanceof Map and keep returning
[...props.entries()] for Map inputs, otherwise fall back to
Object.entries(props) (and still return [] when props is falsy).
- Around line 48-54: getPropSize currently treats any numeric props.size as the
count, which misfires for plain objects that legitimately have a numeric "size"
field; change getPropSize to only treat a numeric size as authoritative for
non-plain-object collections (e.g. Map/Set/Array) and fall back to
Object.keys(props).length for plain objects. Locate the getPropSize function and
add a plain-object check (e.g. Object.prototype.toString.call(props) ===
'[object Object]') and only return props.size when props is not a plain object
(or when props is an Array/Map/Set), otherwise return Object.keys(props).length.

In `@src/review.js`:
- Around line 106-111: The getReviewHistory path constructs a decision object
from propsMap without verifying source/target/edgeType first; mirror the
defensive check used in getPendingSuggestions by ensuring in getReviewHistory
you retrieve source = getProp(propsMap,'source'), target =
getProp(propsMap,'target'), edgeType = getProp(propsMap,'edgeType') and only
build/add the decision (and add to reviewedKeys) when source && target &&
edgeType are truthy, keeping the same `${source}|${target}|${edgeType}` key
construction and avoiding use of incomplete values.
- Around line 273-286: The code is pushing objects with potentially undefined
fields into decisions (see getProp usage and decisions.push), violating the
ReviewDecision typedef; update the logic in src/review.js to validate required
props (at minimum action and any string fields like source, target, reviewer)
before pushing: ensure action is one of 'accept'|'reject'|'adjust'|'skip' and
that required string props are non-empty, and if a node is malformed either skip
it (with a debug/error log) or throw a clear error; reference getProp,
decisions.push and the ReviewDecision shape when implementing the checks so
malformed prop-bags are filtered out before insertion.

In `@test/contracts.integration.test.js`:
- Line 135: The test suite applies SLOW_CLI_TIMEOUT to only 6 of 16 tests
inconsistently (e.g., the "nodes --id" test uses SLOW_CLI_TIMEOUT while "nodes
--json" does not); update the tests so CLI-invoking cases are handled
consistently by either adding the SLOW_CLI_TIMEOUT override to every test that
runs the CLI (search for tests named like "nodes --id" and "nodes --json") or
refactor to use a single helper (e.g., runCliTest or a wrapper around it) that
always applies SLOW_CLI_TIMEOUT to avoid ad-hoc per-test timeouts, and if some
tests genuinely need longer timeouts add brief comments documenting why.
- Line 18: SLOW_CLI_TIMEOUT (15_000) is shorter than the timeout used by runCli
(timeout: 30_000), causing flakiness; fix by making the test harness timeout >=
the CLI exec timeout or lowering the CLI exec timeout to match: either set
SLOW_CLI_TIMEOUT to 30_000 (or higher) or change the timeout passed in
runCli/execFileSync to 15_000 so both timeouts align (update the constants and
the timeout option in runCli accordingly).

In `@test/prop-bag.test.js`:
- Around line 4-36: Add unit tests exercising edge cases for the prop-bag
helpers: call getProp, getPropSize, getPropEntries and toPropObject with null
and undefined and assert they return safe defaults (e.g., undefined for getProp,
0 for getPropSize, empty array for getPropEntries, and empty object for
toPropObject); add tests that getProp returns undefined for non-existent keys;
add a prototype-pollution test by creating an object with inherited properties
(Object.create({ inherited: 'bad' })) and asserting getProp(obj, 'inherited') is
undefined while getProp(obj, 'own') returns the own property; and assert
getPropSize(new Map()) and getPropSize({}) return 0 so getProp, getPropSize,
getPropEntries, and toPropObject are validated against these edge cases.

---

Outside diff comments:
In `@src/export.js`:
- Around line 88-89: The iteration over edge properties uses
Object.entries(edge.props) which bypasses the getPropEntries abstraction and may
break if edge.props can be a Map; update the loop in export.js (the block that
checks "if (edge.props) { for (const [key, value] of
Object.entries(edge.props))") to use getPropEntries(edge.props) instead,
mirroring how node properties are handled elsewhere, and apply the same
replacement in merge.js at the loop around the code referenced on line 108 so
both edge property iterations consistently use getPropEntries.

In `@src/merge.js`:
- Around line 107-113: The loop over edge.props uses Object.entries(edge.props)
which is inconsistent with node prop handling; replace
Object.entries(edge.props) with getPropEntries(edge.props) in merge.js (the
block that checks MERGE_EDGE_PROPS and calls patch.setEdgeProperty) and the
similar spot in export.js to safely support Map or object prop formats; also
ensure getPropEntries is imported/available in those modules and keep the
existing checks for MERGE_EDGE_PROPS and value !== undefined/null before calling
patch.setEdgeProperty.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 743ef6a8-c1cc-4808-b5b6-3f2bcb3462c5

📥 Commits

Reviewing files that changed from the base of the PR and between fc0ef94 and 3964fb8.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (25)
  • AGENTS.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • GUIDE.md
  • ROADMAP.md
  • TECH-PLAN.md
  • docs/README.md
  • docs/design/git-warp-upgrade-audit.md
  • package.json
  • src/cli/commands.js
  • src/content.js
  • src/epoch.js
  • src/export.js
  • src/merge.js
  • src/nodes.js
  • src/prop-bag.js
  • src/review.js
  • src/suggest.js
  • src/views.js
  • test/contracts.integration.test.js
  • test/diff.test.js
  • test/graph.test.js
  • test/import.test.js
  • test/merge.test.js
  • test/prop-bag.test.js

```

git-mind depends on `@git-stunts/git-warp`, which is currently installed from a local path. See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup details.
git-mind depends on the published `@git-stunts/git-warp` package. See [CONTRIBUTING.md](CONTRIBUTING.md) for current development setup details.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify CONTRIBUTING.md mentions the published package model
echo "=== Checking CONTRIBUTING.md for git-warp setup guidance ==="
rg -n -C3 "git-warp|git-stunts" CONTRIBUTING.md | head -30

Repository: flyingrobots/git-mind

Length of output: 307


BLOCKING: GUIDE.md contradicts documented dependency model.

Line 67 claims git-mind "depends on the published @git-stunts/git-warp package," but AGENTS.md and CLAUDE.md explicitly require "git-stunts/git-warp (local path)" as the core dependency for CRDT graph on Git. This mismatch must be resolved before merging. Either:

  1. The documentation is stale and git-warp was never published (revert GUIDE.md to clarify local-path requirement), or
  2. AGENTS.md and CLAUDE.md require updating to reflect the published package model.

Additionally, CLAUDE.md explicitly mandates "git-stunts/plumbing is installed as a direct dependency"—ensure package.json reflects both requirements.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GUIDE.md` at line 67, GUIDE.md currently states git-mind "depends on the
published `@git-stunts/git-warp` package" which contradicts AGENTS.md and
CLAUDE.md that require "git-stunts/git-warp (local path)" and also mandates
"git-stunts/plumbing" as a direct dependency; pick one model and make
consistent: either revert GUIDE.md to state the local-path requirement (update
the `git-stunts/git-warp` phrasing) and ensure CLAUDE.md/AGENTS.md remain
accurate, or update AGENTS.md and CLAUDE.md to reflect the published
`@git-stunts/git-warp` model; finally, ensure package.json lists the chosen
artifacts correctly (add or change entries for "git-stunts/git-warp" and
"git-stunts/plumbing" in dependencies or local path entries) so all three
documents (GUIDE.md, AGENTS.md, CLAUDE.md) and package.json are consistent.

},
"dependencies": {
"@git-stunts/git-warp": "^11.5.0",
"@git-stunts/git-warp": "^14.16.2",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify `@git-stunts/git-warp` v14.16.2 is referenced correctly in package-lock.json
# and check for any version mismatches after npm install

if [ -f "package-lock.json" ]; then
  echo "=== Checking package-lock.json for git-warp version ==="
  cat package-lock.json | jq '.packages["node_modules/@git-stunts/git-warp"].version // .dependencies["@git-stunts/git-warp"].version' 2>/dev/null || echo "Could not parse package-lock.json"
fi

echo ""
echo "=== Searching for git-warp API usage patterns in src/ ==="
rg -n "getNodeProps|createPatch|addNode|addEdge|setProperty|attachContent|discoverTicks|materialize" --type js src/ | head -30

Repository: flyingrobots/git-mind

Length of output: 2347


🏁 Script executed:

# Check prop-bag.js to see how it handles the breaking change
cat -n src/prop-bag.js | head -30

Repository: flyingrobots/git-mind

Length of output: 1014


🏁 Script executed:

# Check if git-stunts/git-warp exists as a local dependency in the repo
find . -type d -name "git-warp" -o -name "git-stunts" | head -20

Repository: flyingrobots/git-mind

Length of output: 47


🏁 Script executed:

# Check the actual package.json to see the current dependencies around git-warp
cat -n package.json | sed -n '25,50p'

Repository: flyingrobots/git-mind

Length of output: 757


Use local file path for @git-stunts/git-warp, not semver version — violates coding guidelines.

Replace "@git-stunts/git-warp": "^14.16.2" with a local path reference per guidelines. The codebase depends on git-warp as the core CRDT graph engine and must pin to your local git-stunts monorepo build, not npm registry versions. The prop-bag.js shim correctly handles the v14 breaking change (Map → plain object in getNodeProps), so API compatibility is not a blocker—but the dependency specification itself is non-compliant.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 29, Replace the semver entry for "@git-stunts/git-warp"
in package.json with a local file: dependency that points at your local
git-stunts monorepo build (i.e. the built/git-warp package in your monorepo) so
the project uses the local CRDT engine instead of an npm version; update the
lockfile by running your package manager install afterwards and verify the
package resolves to the local path (check package.json's "@git-stunts/git-warp"
value and the resolved entry in package-lock.json/yarn.lock).

@flyingrobots
Copy link
Owner Author

@coderabbitai Thanks. Two dependency-model findings on this PR are stale after the governance/docs alignment that already landed on : the published-package model is intentional, and the current branch already reflects it consistently in , , , , and . The actionable feedback on the runtime boundary has been fixed in the latest commits; the local-path recommendation should be treated as a mix-up, not as the target state for this repo.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 25, 2026

Tip

For best results, initiate chat on the files or code changes.

@flyingrobots — understood, and thanks for the correction. The local-path entries in my knowledge base are a mix-up that shouldn't have been there. Removing both now and replacing them with the accurate model.


✏️ Learnings added
Learnt from: flyingrobots
URL: https://github.com/flyingrobots/git-mind/pull/314

Timestamp: 2026-03-25T16:30:07.639Z
Learning: In the flyingrobots/git-mind repository, `git-stunts/git-warp` is intentionally consumed as a **published npm package** (not a local path install). The published-package model is the governed, correct dependency model for this repo and is reflected consistently in AGENTS.md, CLAUDE.md, CONTRIBUTING.md, GUIDE.md, and TECH-PLAN.md. Any review suggestion recommending a local-path install of `git-stunts/git-warp` is incorrect and should be disregarded.

Learnt from: flyingrobots
URL: https://github.com/flyingrobots/git-mind/pull/314

Timestamp: 2026-03-25T16:30:07.639Z
Learning: In the flyingrobots/git-mind repository, the minimum supported Node.js runtime is **>= 22** (not >= 20). This is the current governed requirement reflected in contributor docs (AGENTS.md, CLAUDE.md, CONTRIBUTING.md).

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


⛔ Learnings removed
Learnt from: CR
Repo: flyingrobots/git-mind PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-25T15:00:44.958Z
Learning: Applies to package.json : Use `git-stunts/git-warp` as core dependency (local path, CRDT graph on Git)

Learnt from: CR
Repo: flyingrobots/git-mind PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T15:00:58.215Z
Learning: Applies to package.json : Use git-stunts/git-warp (local path) as the core dependency for CRDT graph on Git

🧠 Learnings used
Learnt from: CR
Repo: flyingrobots/git-mind PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-25T15:00:44.958Z
Learning: Applies to package.json : Use `git-stunts/git-warp` as core dependency (local path, CRDT graph on Git)

Learnt from: CR
Repo: flyingrobots/git-mind PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T15:00:58.215Z
Learning: Applies to package.json : Use git-stunts/git-warp (local path) as the core dependency for CRDT graph on Git

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/epoch.js`:
- Around line 77-83: The current validation in the epoch record block only
checks typeof tick === 'number' which allows NaN, Infinity, and negatives;
update the check around the existing if ( ... ) so tick is further validated
with Number.isFinite(tick) and Number.isInteger(tick) (or Math.floor check) and
tick >= 0 to reject NaN/Infinity/non-integers/negative values, while keeping the
existing fullSha and recordedAt string/length checks; ensure the conditional
uses the same variable names (tick, fullSha, recordedAt) so malformed epoch.tick
values are rejected.

In `@src/review.js`:
- Around line 290-291: Update the numeric validation in the decision-node
validation (replace the current typeof checks for confidence/timestamp) to
reject NaN and infinities by using Number.isFinite(confidence) and
Number.isFinite(timestamp) (or invert with !Number.isFinite) instead of typeof,
e.g., change the condition that currently uses "typeof confidence !== 'number'
|| typeof timestamp !== 'number'" to use Number.isFinite checks so NaN and
+/-Infinity are treated as invalid in the validation logic in src/review.js.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 48feedf7-480f-4a2e-85f0-638483f85145

📥 Commits

Reviewing files that changed from the base of the PR and between 3964fb8 and 4d88845.

📒 Files selected for processing (14)
  • CHANGELOG.md
  • src/cli/commands.js
  • src/content.js
  • src/epoch.js
  • src/export.js
  • src/merge.js
  • src/prop-bag.js
  • src/review.js
  • test/contracts.integration.test.js
  • test/epoch.test.js
  • test/export.test.js
  • test/prop-bag.test.js
  • test/resolve-context.test.js
  • test/review.test.js

Comment on lines +77 to +83
if (
typeof tick !== 'number' ||
typeof fullSha !== 'string' ||
fullSha.length === 0 ||
typeof recordedAt !== 'string' ||
recordedAt.length === 0
) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

tick validation is too weak for malformed records.

Line 78 only checks typeof tick === 'number', so NaN, Infinity, and negative values pass. That can leak invalid epoch.tick into downstream materialization.

Suggested fix
   if (
-    typeof tick !== 'number' ||
+    !Number.isInteger(tick) ||
+    tick < 0 ||
     typeof fullSha !== 'string' ||
     fullSha.length === 0 ||
     typeof recordedAt !== 'string' ||
     recordedAt.length === 0
   ) {
📝 Committable suggestion

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

Suggested change
if (
typeof tick !== 'number' ||
typeof fullSha !== 'string' ||
fullSha.length === 0 ||
typeof recordedAt !== 'string' ||
recordedAt.length === 0
) {
if (
!Number.isInteger(tick) ||
tick < 0 ||
typeof fullSha !== 'string' ||
fullSha.length === 0 ||
typeof recordedAt !== 'string' ||
recordedAt.length === 0
) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/epoch.js` around lines 77 - 83, The current validation in the epoch
record block only checks typeof tick === 'number' which allows NaN, Infinity,
and negatives; update the check around the existing if ( ... ) so tick is
further validated with Number.isFinite(tick) and Number.isInteger(tick) (or
Math.floor check) and tick >= 0 to reject NaN/Infinity/non-integers/negative
values, while keeping the existing fullSha and recordedAt string/length checks;
ensure the conditional uses the same variable names (tick, fullSha, recordedAt)
so malformed epoch.tick values are rejected.

Comment on lines +290 to +291
typeof confidence !== 'number' ||
typeof timestamp !== 'number'
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Pedantic nitpick: NaN slips through numeric validation.

typeof NaN === 'number' evaluates to true, so a decision node with confidence: NaN or timestamp: NaN would pass validation and pollute downstream consumers.

Consider Number.isFinite() for paranoid-level defense:

-      typeof confidence !== 'number' ||
-      typeof timestamp !== 'number'
+      !Number.isFinite(confidence) ||
+      !Number.isFinite(timestamp)

Realistically, this requires deliberate sabotage of graph data. Up to you whether the extra paranoia is warranted.

📝 Committable suggestion

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

Suggested change
typeof confidence !== 'number' ||
typeof timestamp !== 'number'
!Number.isFinite(confidence) ||
!Number.isFinite(timestamp)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/review.js` around lines 290 - 291, Update the numeric validation in the
decision-node validation (replace the current typeof checks for
confidence/timestamp) to reject NaN and infinities by using
Number.isFinite(confidence) and Number.isFinite(timestamp) (or invert with
!Number.isFinite) instead of typeof, e.g., change the condition that currently
uses "typeof confidence !== 'number' || typeof timestamp !== 'number'" to use
Number.isFinite checks so NaN and +/-Infinity are treated as invalid in the
validation logic in src/review.js.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dependency: audit and upgrade @git-stunts/git-warp before major Hill 1 implementation

1 participant