Files
codebase-memory-mcp/tests
Adam Schulte 4e80d2fff7 feat(graph): temporal properties on FILE_CHANGES_WITH edges and File nodes (#257)
Adds three changes that landed together in PR #257, with a JSON-escape
hardening on the new File node properties so unusual file extensions
can't produce malformed output.

Mode A — search_graph results expose node properties_json
---------------------------------------------------------

emit_search_results in src/mcp/mcp.c now calls enrich_node_properties
for each result and parks the parsed yyjson_doc* in an out-array. The
two exit paths in handle_search_graph (success and sq_type_error) walk
that array and yyjson_doc_free each entry after yy_doc_to_str runs, so
zero-copy strings stay alive until serialization completes. Callers no
longer have to round-trip through get_code_snippet just to inspect a
node's properties.

Mode B — Cypher bare edge return surfaces full properties JSON
--------------------------------------------------------------

binding_get_virtual in src/cypher/cypher.c returns e->properties_json
(or "{}" if absent) when prop is NULL, instead of "". `RETURN r` on
an edge variable is now actually useful for inspecting timestamps,
weights, etc. without naming each property up front.

Temporal data on FILE_CHANGES_WITH edges and File nodes
-------------------------------------------------------

cbm_change_coupling_t gains last_co_change. cbm_commit_files_t and
the internal commit_t gain a timestamp field. Both git-log paths
(libgit2 and popen --pretty=format:COMMIT:%H:%ct) populate it.
cbm_compute_change_coupling carries a parallel pair_timestamps hash
table and writes the max timestamp to last_co_change. The edge
properties JSON now includes "last_co_change":<unix-epoch>.

cbm_pipeline_githistory_compute additionally aggregates per-file
change_count and last_modified into a new cbm_file_temporal_t array
exposed via result->file_temporal. cbm_pipeline_githistory_apply
upserts those values onto matching File nodes so callers can run
hotspot queries (most-changed, recently-changed) directly. Both the
sequential cbm_pipeline_pass_githistory and the parallel run_githistory
in pipeline.c free file_temporal and trigger apply when either
couplings or temporal entries are present.

The extension stored on the File node properties is now JSON-escaped
via cbm_json_escape before snprintf'ing into the props blob. The
original PR concatenated the raw extension; a hypothetical path with
a quote or backslash in the extension would have produced malformed
JSON. Practical risk was tiny (real extensions never contain those
characters) but the escape is cheap and correct.

Tests
-----

- githistory_coupling_carries_last_co_change pins last_co_change as
  the max of the per-pair commit timestamps even when an unrelated
  pair sits between them in the input order.
- cypher_bare_edge_return_exposes_properties_json regresses Mode B
  by asserting `RETURN r` on an HTTP_CALLS edge surfaces its url_path
  in the response.
- tool_search_graph_includes_node_properties regresses Mode A by
  asserting search_graph's response contains the signature/return_type/
  is_exported keys from a HandleRequest node's properties_json.

Existing githistory tests had their cbm_commit_files_t initializers
extended with a zero timestamp (backward-compatible — last_co_change
falls back to 0 for callers that don't supply timestamps).

Cherry-picked from #257; renamed the timestamp field types to
long long for portability across 32-/64-bit time_t platforms, used
the existing cbm_json_escape utility instead of raw concatenation
for the extension, and added explicit tests for all three changes.

Closes #257.

Co-authored-by: Adam Schulte <adamschulte148@gmail.com>
2026-05-09 01:15:03 +02:00
..
2026-04-15 23:56:03 +02:00