Skip to content

fix: tighten graph freshness follow-up#480

Merged
mohanagy merged 3 commits into
nextfrom
issue-479-freshness-followup
Jun 2, 2026
Merged

fix: tighten graph freshness follow-up#480
mohanagy merged 3 commits into
nextfrom
issue-479-freshness-followup

Conversation

@mohanagy
Copy link
Copy Markdown
Owner

@mohanagy mohanagy commented Jun 2, 2026

Summary

  • persist graph build freshness metadata and use git build-vs-current diffing instead of graph/source mtimes
  • narrow governance freshness receipts so they no longer expose graph paths, and remove Graph source from text summaries
  • add honest shared-route coverage plus git freshness regressions for preserved mtimes, non-file source paths, and spaced file paths

Testing

  • npm run test:run -- tests/unit/freshness-surfaces.test.ts
  • npm run typecheck
  • npm run build
  • CI=1 npm run test:run

Closes #479

Summary by CodeRabbit

  • Documentation

    • Expanded guidance on graph freshness receipts, governance receipt contents, enforcement flags, and session diagnostics across README and docs.
  • New Features

    • Richer freshness evaluation and reporting using persisted build metadata and Git-backed detection; includes selected-context counters and timestamps.
  • Bug Fixes

    • Removed local graph path from governance receipts to avoid exposing filesystem details.
  • Tests

    • Updated and added tests for freshness detection, Git fixtures, and receipt/output formatting.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6fb1824c-c911-4469-99b1-3a89dd535e22

📥 Commits

Reviewing files that changed from the base of the PR and between 21a6874 and 5c59172.

📒 Files selected for processing (2)
  • src/runtime/implementation-pack.ts
  • tests/unit/implementation-pack.test.ts

📝 Walkthrough

Walkthrough

Removes graph_path from governance receipts, adds graph-build freshness metadata (git/filesystem) at generation time, propagates it through build/export, and refactors runtime freshness checks to use persisted metadata with git, filesystem, and legacy fallbacks.

Changes

Graph Freshness Source of Truth and Privacy Contract

Layer / File(s) Summary
Documentation and contract updates
README.md, docs/agent-governance.md, docs/concepts/context-packs.md, docs/reference/cli-and-mcp.md, src/contracts/context-pack.ts
Updated docs to describe git-vs-filesystem freshness computation and removed graph_path from the ContextPackGovernanceGraphFreshness interface.
Git snapshot and diff helpers
src/shared/git.ts
Added readGitSnapshot(projectDir) and diffGitFilesBetweenCommits(projectDir, fromSha, toSha) with path normalization, parsing, dedupe/sort, and safe project-relative mapping.
Build freshness metadata generation and validation
src/shared/graph-build-freshness.ts
Introduced GraphBuildFreshnessMetadata (git/filesystem strategies), path normalization, content fingerprinting, buildGraphBuildFreshnessMetadata, and isGraphBuildFreshnessMetadata validator.
Graph generation and export of build freshness
src/infrastructure/generate.ts, src/pipeline/build.ts, src/pipeline/export.ts
Collect source_file values during generation, set graph.graph.graph_build_freshness, propagate graph_build_freshness via buildFromJson when present, and include it in serialized JSON when valid.
Freshness evaluation using persisted metadata and strategies
src/runtime/freshness.ts
Refactored indexing/normalization and implemented change-detection strategies: git (snapshot/diff + fingerprint validation), filesystem (fingerprint comparison), and legacy mtime fallback; prefer persisted generated timestamps for output.
Remove graph_path from governance receipt and display surfaces
src/runtime/context-pack-governance.ts, src/infrastructure/context-pack-command.ts
Removed graph_path from governance receipt construction and from CLI/context-pack freshness summary output.
Test fixtures and freshness surface validation
tests/unit/answer-ready-explain-pack.test.ts, tests/unit/handoff-command.test.ts, tests/unit/freshness-surfaces.test.ts, tests/unit/graph-build-freshness.test.ts
Updated fixtures to drop graph_path, added graph-build-freshness unit tests, and added git-backed fixtures and tests exercising selected-context drift, dirty files, diffs, and updated textual assertions.

Sequence Diagram (high-level flow)

sequenceDiagram
  participant Generator as generateGraph
  participant BuildMeta as buildGraphBuildFreshnessMetadata
  participant Git as readGitSnapshot/diffGitFilesBetweenCommits
  participant Freshness as analyzeGraphContextFreshness
  participant Export as buildContextPackGovernanceReceipt
  Generator->>BuildMeta: supply collected source_file list
  BuildMeta->>Git: attempt repo snapshot (headSha + dirtyFiles)
  Git-->>BuildMeta: return headSha and dirtyFiles
  BuildMeta-->>Generator: attach graph_build_freshness to graph
  Freshness->>BuildMeta: prefer persisted generated_at/generated_ms
  Freshness->>Git: request diffs/snapshot for git strategy
  Freshness-->>Export: produce graph_freshness without graph_path
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #479 — This PR implements the requested fixes: uses git revision + working-tree diff as freshness source for git repos, removes graph_path from share-safe governance receipts, adds selected-context regression tests, and updates CLI/MCP/docs surfaces.

Possibly related PRs

  • mohanagy/madar#448 — Both touch governance receipt graph-freshness modeling; this PR removes graph_path that was present in the prior contract.
  • mohanagy/madar#478 — Both PRs refine the graph freshness contract; this PR removes graph_path and adds build-time metadata capture building on that infrastructure.

Poem

🐰 I sniffed the graph at build-time, found its git and seed,
I kept its secrets safe — no paths to leak or read.
Dirty files murmured, commits told the tale,
Freshness counts recorded without a breadcrumb trail.
Hop, hop — the rabbit guards the graph with care.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: tighten graph freshness follow-up' clearly summarizes the main change: addressing graph freshness handling as a follow-up fix, which aligns with the issue #479 context.
Description check ✅ Passed The PR description includes all required template sections: Summary (with bullet points detailing changes), Testing (with specific commands), Checklist items confirmed, and Related issues (Closes #479). Content is complete and specific.
Linked Issues check ✅ Passed The PR successfully implements all four coding requirements from issue #479: (1) git-based freshness diffing replaces mtime checks in freshness.ts [#479], (2) graph_path removed from governance receipts in context-pack.ts and context-pack-governance.ts [#479], (3) new git freshness tests added for dirty files and mtime scenarios in freshness-surfaces.test.ts [#479], (4) documentation and CLI surfaces updated in multiple docs and runtime files [#479].
Out of Scope Changes check ✅ Passed One change appears out of scope: implementation-pack.ts adds server-action path pattern and scoring logic unrelated to graph freshness tightening. However, the accompanying test provides focused validation. This represents minimal scope creep suitable for a single-pass code review.

✏️ 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 issue-479-freshness-followup

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

Copy link
Copy Markdown

@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: 3

🧹 Nitpick comments (2)
tests/unit/freshness-surfaces.test.ts (1)

390-575: 💤 Low value

Repeated analyzeGraphContextFreshness type-cast block across the new cases.

Each of the five new git-drift tests re-declares the same dynamic-import-plus-type-assertion preamble (lines 391-402, 428-439, 465-476, 502-513, 541-552). Extracting a small loadAnalyzeGraphContextFreshness() helper would remove the duplication and keep the cases focused on their distinct fixtures/assertions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/freshness-surfaces.test.ts` around lines 390 - 575, The tests
repeat the same dynamic-import and type-assertion for
analyzeGraphContextFreshness across multiple cases; extract that logic into a
small helper (e.g., loadAnalyzeGraphContextFreshness) that performs the import
of '../../src/runtime/freshness.js' and returns the typed
analyzeGraphContextFreshness function, then replace the repeated blocks in each
test with a call to this helper (referencing analyzeGraphContextFreshness and
the helper name) so each test only creates its fixture and assertions.
src/shared/git.ts (1)

67-94: 💤 Low value

Consider logging git command failures for debugging.

Both readGitSnapshot and diffGitFilesBetweenCommits silently return null or [] when git commands fail. This makes it difficult to diagnose issues like:

  • Repository corruption
  • Invalid commit SHAs
  • Git not installed or not in PATH
  • Permission issues

Since these functions are used for freshness detection (which can fall back to mtime/fingerprint strategies), silent failures might be acceptable. However, adding debug-level logging would help troubleshoot freshness-related issues in production.

Also applies to: 96-119

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shared/git.ts` around lines 67 - 94, Update readGitSnapshot and
diffGitFilesBetweenCommits to log git command failures at debug level instead of
failing silently: catch the thrown error in each function's try/catch and call
the shared logger (or processLogger) with a clear message including the function
name (readGitSnapshot / diffGitFilesBetweenCommits), the git command context
(e.g., rev-parse or status or diff args) and the error details/stack so
developers can diagnose git not found, bad SHAs, permission issues, etc.; keep
the current return behavior (null or []) but ensure the debug log includes
repoRoot, projectDir/commit IDs and the actual error message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/shared/git.ts`:
- Around line 40-49: decodeGitPath currently only handles '\"' and '\\' escapes
and fails for C-style escapes and octal sequences from git porcelain output;
update the decodeGitPath function to, when the input is a quoted path, unquote
and scan the inner string interpreting backslash sequences: handle \" and \\ as
before, map \n, \r, \t (and other common C escapes like \b, \f, \v if desired)
to their control characters, and decode octal escapes \NNN (up to three octal
digits) into the corresponding byte/char, returning the fully unescaped string;
alternatively you may switch callers to use git porcelain with -z to avoid
quoted/escaped paths, but if keeping decodeGitPath implement a small stateful
parser in decodeGitPath to replace the current replaceAll-based logic.

In `@src/shared/graph-build-freshness.ts`:
- Around line 25-43: The normalizeSourceFile function currently returns an
absolute path for inputs that escape the project root (in the branch that checks
relativePath === '..' || relativePath.startsWith(`..${sep}`) ||
isAbsolute(relativePath)); change that branch to return an empty string instead
of resolve(trimmed).replaceAll('\\', '/'), so escaped paths are treated like
other invalid/empty results; keep all other behavior (trim, convert separators
with replaceAll, and the checks for empty/'.') the same.
- Around line 49-57: In fileContentFingerprint, the non-file branch currently
embeds the absolute filePath into the hash which makes fingerprints machine- and
location-specific; change the decision to use a portable identifier by replacing
the absolute path with a project-relative or normalized path (or omit the path
entirely) before feeding it to createHash so fingerprints are stable across
machines; locate the non-file branch in fileContentFingerprint (uses statSync,
createHash, and stats.mtimeMs/stats.size) and compute a relativePath (e.g.,
path.relative(projectRoot, filePath) or path.normalize) or drop the path
component, then update the string passed to createHash accordingly.

---

Nitpick comments:
In `@src/shared/git.ts`:
- Around line 67-94: Update readGitSnapshot and diffGitFilesBetweenCommits to
log git command failures at debug level instead of failing silently: catch the
thrown error in each function's try/catch and call the shared logger (or
processLogger) with a clear message including the function name (readGitSnapshot
/ diffGitFilesBetweenCommits), the git command context (e.g., rev-parse or
status or diff args) and the error details/stack so developers can diagnose git
not found, bad SHAs, permission issues, etc.; keep the current return behavior
(null or []) but ensure the debug log includes repoRoot, projectDir/commit IDs
and the actual error message.

In `@tests/unit/freshness-surfaces.test.ts`:
- Around line 390-575: The tests repeat the same dynamic-import and
type-assertion for analyzeGraphContextFreshness across multiple cases; extract
that logic into a small helper (e.g., loadAnalyzeGraphContextFreshness) that
performs the import of '../../src/runtime/freshness.js' and returns the typed
analyzeGraphContextFreshness function, then replace the repeated blocks in each
test with a call to this helper (referencing analyzeGraphContextFreshness and
the helper name) so each test only creates its fixture and assertions.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 508cb940-8d8b-4514-96e6-bba2e7340302

📥 Commits

Reviewing files that changed from the base of the PR and between 813f8d7 and c209994.

📒 Files selected for processing (16)
  • README.md
  • docs/agent-governance.md
  • docs/concepts/context-packs.md
  • docs/reference/cli-and-mcp.md
  • src/contracts/context-pack.ts
  • src/infrastructure/context-pack-command.ts
  • src/infrastructure/generate.ts
  • src/pipeline/build.ts
  • src/pipeline/export.ts
  • src/runtime/context-pack-governance.ts
  • src/runtime/freshness.ts
  • src/shared/git.ts
  • src/shared/graph-build-freshness.ts
  • tests/unit/answer-ready-explain-pack.test.ts
  • tests/unit/freshness-surfaces.test.ts
  • tests/unit/handoff-command.test.ts
💤 Files with no reviewable changes (5)
  • src/runtime/context-pack-governance.ts
  • tests/unit/handoff-command.test.ts
  • src/infrastructure/context-pack-command.ts
  • src/contracts/context-pack.ts
  • tests/unit/answer-ready-explain-pack.test.ts

Comment thread src/shared/git.ts Outdated
Comment thread src/shared/graph-build-freshness.ts
Comment thread src/shared/graph-build-freshness.ts
mohanagy and others added 2 commits June 2, 2026 21:47
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy mohanagy merged commit 551985a into next Jun 2, 2026
7 checks passed
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.

1 participant