Skip to content

fix(toolpath-convo): consult git HEAD for Write-tool before-state#48

Merged
eliothedeman merged 1 commit into
mainfrom
eliot/issue-35-write-before-state
Apr 22, 2026
Merged

fix(toolpath-convo): consult git HEAD for Write-tool before-state#48
eliothedeman merged 1 commit into
mainfrom
eliot/issue-35-write-before-state

Conversation

@eliothedeman
Copy link
Copy Markdown
Collaborator

Summary

  • toolpath_convo::file_write_diff gains a before_state: Option<&str> parameter. For the Write { content } shape, Some(prior) becomes the diff pre-image; None preserves the old behaviour (diff against ""). Edit/MultiEdit paths are unchanged — they already carry their own old_string/new_string.
  • toolpath-claude's Claude-JSONL deriver wires a best-effort git-HEAD lookup for Write invocations: shells out to git show HEAD:<relpath> using the turn's cwd (falls back to the conversation's project path). Silently returns None when the project isn't a git repo, the file isn't tracked at HEAD, or git isn't on PATH — the deriver then produces the existing additions-only diff.
  • toolpath-convo bumped to 0.7.0 (public API break).

Closes #35

Test plan

  • Unit: file_write_diff with Some(before) shows - lines for replaced content
  • Unit: file_write_diff with None is addition-only (existing behaviour preserved)
  • Unit: before_state ignored for Edit shape
  • Unit: resolve_local_dir precedence (entry cwd > config > conversation), file:// prefix stripping
  • Integration: real tempdir git init + commit + Claude Write tool → derived raw diff has -old-content and +new-content
  • Integration: no-git tempdir + Claude Write → additions-only fallback (no - lines)
  • cargo test --workspace (1209 passed, 0 failed)
  • cargo clippy --workspace -- -D warnings clean
  • All 12 example documents still validate

`file_write_diff` now takes a `before_state: Option<&str>` parameter.
For the `Write { content }` shape, when `Some` it becomes the diff
pre-image — so overwriting an existing file shows real `-`/`+` lines
instead of an addition-only hunk. `None` preserves the old behaviour
(diff against `""`), correct for new files.

`toolpath-claude`'s Claude-JSONL deriver wires a best-effort git
lookup for Write invocations: shells out to `git show HEAD:<relpath>`
using the turn's `cwd` (with fallback to the conversation's project
path). Silently falls back to the additions-only diff when the
project isn't a git repo, the file isn't tracked at HEAD, or `git`
isn't on PATH.

Edit / MultiEdit are unchanged — they already carry their own
`old_string`/`new_string`.

Closes #35
@github-actions
Copy link
Copy Markdown

🔍 Preview deployed: https://df5cbd6d.toolpath.pages.dev

Copy link
Copy Markdown
Collaborator Author

@eliothedeman eliothedeman left a comment

Choose a reason for hiding this comment

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

Summary

Cleanly fixes #35 by threading before_state: Option<&str> through file_write_diff / file_write_change and wiring a best-effort git show HEAD:<rel> lookup at the Claude call site. Public API break on toolpath-convo is properly versioned (0.7.0) and all four required locations are updated.

Notes

  • crates/toolpath-claude/src/derive.rs:22-44git_head_content uses Command::new("git").arg(...) with -C, so no shell and no injection surface. format!("HEAD:{rel_str}") is passed as a single argv element. Safe.
  • crates/toolpath-claude/src/derive.rs:51-60resolve_local_dir precedence (entry cwd > config > convo) is the right call per-turn; file:// stripping is handled once at resolve time.
  • crates/toolpath-claude/src/derive.rs:533-553 — only Writes consult git; Edit/MultiEdit correctly skip the lookup. .and_then chain means any failure (no repo / untracked / git missing) falls through to additions-only.
  • crates/toolpath-convo/src/derive.rs:175-179 — shared deriver passes None with an explanatory comment about providers with local checkout access overriding. Clean boundary.
  • Tests: both paths covered — unit tests for Some/None/Edit-ignored, plus two real tempfile + git init integration tests asserting -old-content / no removal lines. Matches the rubric.
  • Versioning: crates/toolpath-convo/Cargo.toml, workspace Cargo.toml, site/_data/crates.json, CHANGELOG.md all updated to 0.7.0. Cargo.lock reflects it. CI green.
  • No scope creep; no feature flags or backcompat shims.

Verdict

Approve (posted as comment — GitHub blocks self-approval). Surgical fix, correct API shape, safe subprocess call, symmetric tests, versioning complete.

@eliothedeman eliothedeman merged commit 46798ea into main Apr 22, 2026
2 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.

toolpath-convo: Write tool on existing file produces addition-only diff

1 participant