Skip to content

fix(git): honor the stored lfs_policy on materialize/hydrate (P6-GIT-04)#80

Merged
Reederey87 merged 1 commit into
mainfrom
fix/p6-git-04
Jul 4, 2026
Merged

fix(git): honor the stored lfs_policy on materialize/hydrate (P6-GIT-04)#80
Reederey87 merged 1 commit into
mainfrom
fix/p6-git-04

Conversation

@Reederey87

@Reederey87 Reederey87 commented Jul 4, 2026

Copy link
Copy Markdown
Owner

P6-GIT-04 — honor the stored lfs_policy on the eager materialize/hydrate path

Only the worktree path honored lfs_policy; hydrateProjectUnlocked (the primary devstrap sync/materialize clone) never read project.LFSPolicy. And gitEnv sets GIT_CONFIG_GLOBAL=/dev/null, hiding the user's global git lfs install smudge filter — so an lfs-policy=always repo materialized as 3-line pointer files, which match the index → DirtyState clean → recorded available/clean with zero warning, breaking "after sync the tree is really present."

Change

  • internal/git/git.goRunner.LFSInstallLocal (git lfs install --local); required because the global filter is hidden.
  • internal/cli/hydrate.goapplyMaterializeLFSPolicy mirrors applyWorktreeLFSPolicy: always/agent → install + LFSPull (fail the project on error); auto/never → warn. LFSPull carries the P6-GIT-01 long-transfer timeout.
  • internal/cli/materialize.go — applied in materializeGitRepo (the caller), not inside hydrateProjectUnlocked.

Why the caller, not hydrateProjectUnlocked (review-caught blocking bug)

hydrateProjectUnlocked is shared with createFreshWorktree. Placing LFS inside it (a) fired on the worktree flow and (b) missed the retry: a repo recorded "failed" for an LFS pull failure is re-queued via SkeletonProjects (which includes "failed") on the next sync/run-loop tick, hits the already-on-disk early-return, and silently flipped back to "available"/"clean" with pointers. Applying it in materializeGitRepo covers the fresh clone and the retry, and leaves the worktree path untouched.

Tests

TestMaterializeLFSAutoRecordsAvailableWithPointers, …AlwaysRecordsFailedWhenPullFails, …AlwaysDoesNotFlipFailedToAvailableOnRetry (the retry invariant), …NoLFSUnaffected; the pre-existing worktree-cleanup test stays green.

Validation

gofmt clean · spec-drift passes · full go test ./... green. Independent opus review (one blocking finding, fixed) + Codex implementation.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Eager repository materialization now respects the saved LFS setting during clone and hydration.
    • Repositories can now apply local Git LFS setup automatically when needed.
  • Bug Fixes

    • LFS failures are now recorded as failed materializations instead of being treated as successful.
    • Retry behavior no longer flips previously failed LFS-related materializations into a clean state.
    • Documentation and status counts were updated to reflect the latest shipped work.

@Reederey87 Reederey87 enabled auto-merge (squash) July 4, 2026 11:36
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR implements P6-GIT-04, enforcing the stored lfs_policy during eager materialize/hydrate. A new Runner.LFSInstallLocal method and applyMaterializeLFSPolicy helper are wired into materializeGitRepo, with new regression tests and corresponding spec, audit, and work-log documentation updates.

Changes

LFS Policy Enforcement on Materialize/Hydrate

Layer / File(s) Summary
Git runner LFS install support
internal/git/git.go
Adds Runner.LFSInstallLocal, wrapping git lfs install --local for the materialize path.
applyMaterializeLFSPolicy helper
internal/cli/hydrate.go
Adds a logging import and applyMaterializeLFSPolicy, which handles always/agent (install + pull), auto/never (warning), and invalid policy values.
Wiring policy check into materializeGitRepo
internal/cli/materialize.go
Calls applyMaterializeLFSPolicy after hydrateProjectUnlocked, marking the project failed/unknown and returning early on error.
Hydrate LFS materialization tests
internal/cli/hydrate_lfs_test.go
Adds tests covering auto/always policies, pull failure, retry-does-not-flip-to-available, and no-LFS scenarios, with helper utilities for setup, remote creation, options, and assertions.
Worktree test policy override helper
internal/cli/worktree_test.go
Updates the LFS pull failure test to init with auto policy then force lfs_policy to always via new setProjectLFSPolicy SQLite helper.
Spec, audit, and work-log updates
spec/08_GIT_MATERIALIZATION_AND_WORKTREES.md, spec/13_CLI_DAEMON_API.md, spec/18_WORK_LOG.md, docs/audits/README.md
Documents the new LFS-policy enforcement and hydrate clone mechanics, adds a 2026-07-04 work-log entry, and moves P6-GIT-04 to shipped with adjusted open-finding counts.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant materializeGitRepo
  participant hydrateProjectUnlocked
  participant applyMaterializeLFSPolicy
  participant Runner
  participant Store

  materializeGitRepo->>hydrateProjectUnlocked: clone/hydrate local repo
  hydrateProjectUnlocked-->>materializeGitRepo: success
  materializeGitRepo->>applyMaterializeLFSPolicy: apply stored lfs_policy
  applyMaterializeLFSPolicy->>applyMaterializeLFSPolicy: check usesLFS, normalize policy
  alt policy is always/agent
    applyMaterializeLFSPolicy->>Runner: LFSInstallLocal
    applyMaterializeLFSPolicy->>Runner: LFSPull
    Runner-->>applyMaterializeLFSPolicy: error or success
  else policy is auto/never
    applyMaterializeLFSPolicy->>applyMaterializeLFSPolicy: log warning (pointers remain)
  end
  applyMaterializeLFSPolicy-->>materializeGitRepo: return nil or error
  alt error occurred
    materializeGitRepo->>Store: UpdateProjectLocalState(failed, unknown)
    materializeGitRepo-->>materializeGitRepo: return error, skip rebuild/env hydration
  else success
    materializeGitRepo->>materializeGitRepo: continue to dependency rebuild / env hydration
  end
Loading

Possibly related PRs

  • Reederey87/DevStrap#69: Both PRs modify internal/cli/materialize.go's eager materializeGitRepo flow around hydrate/env handling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Clear and specific; it accurately summarizes the main change to honor stored lfs_policy during materialize/hydrate.
Description check ✅ Passed It includes the core summary, tests, and validation details, with only the template's exact headings and checklist formatting missing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 fix/p6-git-04

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

The eager materialize/hydrate path never read project.LFSPolicy, so an
lfs-policy=always repo materialized as silent pointer files recorded
available/clean. Add Runner.LFSInstallLocal (install --local, required
because gitEnv hides the global smudge filter) and applyMaterializeLFSPolicy,
applied in materializeGitRepo (not inside hydrateProjectUnlocked, which the
worktree path shares) so it covers the fresh clone and the SkeletonProjects
retry of a repo recorded failed — never flipping it back to available with
pointers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Reederey87 Reederey87 merged commit e2e5c31 into main Jul 4, 2026
6 of 7 checks passed
@Reederey87 Reederey87 deleted the fix/p6-git-04 branch July 4, 2026 11:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
internal/cli/hydrate.go (1)

224-254: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Extract the shared LFS policy dispatch
applyWorktreeLFSPolicy and applyMaterializeLFSPolicy still duplicate the same auto/never vs always/agent normalization and branching; keep the path-specific LFSInstallLocal step separate, but move the shared policy switch into one helper to avoid the two paths drifting again.

🤖 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 `@internal/cli/hydrate.go` around lines 224 - 254, `applyWorktreeLFSPolicy` and
`applyMaterializeLFSPolicy` still duplicate the same LFSPolicy normalization and
switch logic, so extract the shared `auto/never` vs `always/agent` dispatch into
a helper and reuse it from both paths. Keep the path-specific `LFSInstallLocal`
call only in `applyMaterializeLFSPolicy`, and have both callers delegate the
common policy decision to the new helper so `dsgit.UsesLFS`, `LFSPull`, and the
warning/default handling stay aligned.
internal/cli/hydrate_lfs_test.go (1)

107-136: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate remote-seeding logic with worktree_test.go.

createHydrateRemote re-implements the same bare-repo/seed/push sequence already present in setupFreshWorktreeRepo (internal/cli/worktree_test.go, lines 174-211), differing only in whether LFS-tracked content is committed. Consider extracting a shared helper (e.g., newTestGitRemote(t, opts...)) parameterized by whether to add tracked LFS content, to avoid drift between the two copies as git setup evolves.

🤖 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 `@internal/cli/hydrate_lfs_test.go` around lines 107 - 136,
`createHydrateRemote` duplicates the same bare-repo/seed/push setup already used
by `setupFreshWorktreeRepo`, so refactor the shared git-remote bootstrapping
into a common helper and keep only the LFS-specific file setup in
`createHydrateRemote`. Extract the repeated remote initialization logic into a
helper such as a test git remote builder that both `createHydrateRemote` and
`setupFreshWorktreeRepo` can call, with a parameter or option to add LFS-tracked
content when needed. Ensure the helper owns the shared init/config/commit/push
flow so the two test paths cannot drift.
internal/cli/worktree_test.go (1)

238-265: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Centralize this test-only mutation

Raw SQL here still couples the test to git_repos/namespace_entries schema details and SQLite DSN plumbing. A small test-only setter on state.Store would keep that knowledge in one place.

🤖 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 `@internal/cli/worktree_test.go` around lines 238 - 265, The test helper
setProjectLFSPolicy currently reaches into the database with raw SQL and SQLite
connection setup, which duplicates schema and DSN details inside the test. Move
this mutation into a small test-only setter on state.Store and have
setProjectLFSPolicy call that store method instead, so the knowledge of
git_repos, namespace_entries, and the SQLite plumbing stays centralized.
🤖 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 `@internal/cli/hydrate.go`:
- Around line 248-249: The warning in the hydrate path treats both auto and
never the same, but never is an intentional opt-out and should not suggest
switching to always. Update the policy handling in hydrate.go around the switch
on policy so the code that logs from logging.Logger(ctx).Warn distinguishes auto
from never, keeping the fetch-objects recommendation only for auto and either
suppressing or changing the message for never to reflect the explicit choice.
Use the existing policy variable and the materialize/sync warning path to locate
the change.

---

Nitpick comments:
In `@internal/cli/hydrate_lfs_test.go`:
- Around line 107-136: `createHydrateRemote` duplicates the same
bare-repo/seed/push setup already used by `setupFreshWorktreeRepo`, so refactor
the shared git-remote bootstrapping into a common helper and keep only the
LFS-specific file setup in `createHydrateRemote`. Extract the repeated remote
initialization logic into a helper such as a test git remote builder that both
`createHydrateRemote` and `setupFreshWorktreeRepo` can call, with a parameter or
option to add LFS-tracked content when needed. Ensure the helper owns the shared
init/config/commit/push flow so the two test paths cannot drift.

In `@internal/cli/hydrate.go`:
- Around line 224-254: `applyWorktreeLFSPolicy` and `applyMaterializeLFSPolicy`
still duplicate the same LFSPolicy normalization and switch logic, so extract
the shared `auto/never` vs `always/agent` dispatch into a helper and reuse it
from both paths. Keep the path-specific `LFSInstallLocal` call only in
`applyMaterializeLFSPolicy`, and have both callers delegate the common policy
decision to the new helper so `dsgit.UsesLFS`, `LFSPull`, and the
warning/default handling stay aligned.

In `@internal/cli/worktree_test.go`:
- Around line 238-265: The test helper setProjectLFSPolicy currently reaches
into the database with raw SQL and SQLite connection setup, which duplicates
schema and DSN details inside the test. Move this mutation into a small
test-only setter on state.Store and have setProjectLFSPolicy call that store
method instead, so the knowledge of git_repos, namespace_entries, and the SQLite
plumbing stays centralized.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 365118f3-a34f-4f64-90ee-ee0c5755c52d

📥 Commits

Reviewing files that changed from the base of the PR and between eaf8ae4 and 9422a62.

📒 Files selected for processing (9)
  • docs/audits/README.md
  • internal/cli/hydrate.go
  • internal/cli/hydrate_lfs_test.go
  • internal/cli/materialize.go
  • internal/cli/worktree_test.go
  • internal/git/git.go
  • spec/08_GIT_MATERIALIZATION_AND_WORKTREES.md
  • spec/13_CLI_DAEMON_API.md
  • spec/18_WORK_LOG.md

Comment thread internal/cli/hydrate.go
Comment on lines +248 to +249
case "auto", "never":
logging.Logger(ctx).Warn("git LFS pointer files remain; set lfs_policy=always to fetch objects", "path", project.Path, "lfs_policy", policy)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

never policy gets a misleading "switch to always" warning.

never is an explicit opt-out, not an oversight like auto, yet it gets the same "set lfs_policy=always to fetch objects" warning on every materialize/sync run. This creates persistent, actionable-sounding log noise for a deliberate configuration choice.

💡 Proposed fix to differentiate `never` from `auto`
 	case "auto", "never":
-		logging.Logger(ctx).Warn("git LFS pointer files remain; set lfs_policy=always to fetch objects", "path", project.Path, "lfs_policy", policy)
+		if policy == "auto" {
+			logging.Logger(ctx).Warn("git LFS pointer files remain; set lfs_policy=always to fetch objects", "path", project.Path, "lfs_policy", policy)
+		}
📝 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
case "auto", "never":
logging.Logger(ctx).Warn("git LFS pointer files remain; set lfs_policy=always to fetch objects", "path", project.Path, "lfs_policy", policy)
case "auto", "never":
if policy == "auto" {
logging.Logger(ctx).Warn("git LFS pointer files remain; set lfs_policy=always to fetch objects", "path", project.Path, "lfs_policy", policy)
}
🤖 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 `@internal/cli/hydrate.go` around lines 248 - 249, The warning in the hydrate
path treats both auto and never the same, but never is an intentional opt-out
and should not suggest switching to always. Update the policy handling in
hydrate.go around the switch on policy so the code that logs from
logging.Logger(ctx).Warn distinguishes auto from never, keeping the
fetch-objects recommendation only for auto and either suppressing or changing
the message for never to reflect the explicit choice. Use the existing policy
variable and the materialize/sync warning path to locate the change.

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