Skip to content

Ground explicit file edits in real file contents#34

Merged
CoreyRDean merged 1 commit into
mainfrom
fix/file-edit-grounding
May 21, 2026
Merged

Ground explicit file edits in real file contents#34
CoreyRDean merged 1 commit into
mainfrom
fix/file-edit-grounding

Conversation

@CoreyRDean
Copy link
Copy Markdown
Owner

@CoreyRDean CoreyRDean commented May 17, 2026

Non-technical summary

This fixes a reliability gap in intent's natural-language mode for file edits. When a user names a real file and asks intent to change it, the model can no longer skip straight to an ungrounded snippet that never touches the file. intent now forces that workflow to read the target file first or fail closed.

That matters now because issue #22 is a direct break in the product promise: a request to modify a known file should yield an executable edit grounded in the file's actual contents, not pseudocode.

Technical summary

  • add an engine-side grounding check that detects explicit file-edit requests and tracks whether the model has called read_file on the named target before returning a command or script
  • if the model skips grounding, feed back a repair instruction inside the same turn so it can recover with a read_file tool call; if it still does not, return a refusal instead of surfacing the ungrounded edit
  • strengthen the system prompt in internal/model/prompt.go so explicit file edits require read_file(path) FIRST
  • add regression coverage in internal/engine/engine_test.go for both the repair path and the fail-closed path, and update docs/SPEC.md to document the contract
  • verification: PATH="/opt/homebrew/bin:$PATH" go test ./internal/engine ./internal/model ./internal/cli, PATH="/opt/homebrew/bin:$PATH" go test ./..., PATH="/opt/homebrew/bin:$PATH" go vet ./..., PATH="/opt/homebrew/bin:$PATH" make build
  • breaking changes: none intended; this tightens a buggy edge case so ungrounded file-edit proposals are repaired or rejected instead of shown to the user

Additional notes

Trade-off: the detector is intentionally narrow and keys off explicit edit verbs plus file-like targets. It improves the real bug path without trying to infer every possible mutation request shape in one increment.

Deferred: broader grounding around implicit targets, multi-file rewrite flows, or richer path resolution heuristics remains separate work.

Remaining gap: this ensures explicit file-edit requests must read the named file before proposing a mutation, but it does not redesign the wider prompt/tooling strategy for all mutation tasks.

@CoreyRDean CoreyRDean marked this pull request as ready for review May 21, 2026 13:13
@CoreyRDean CoreyRDean merged commit a29fec5 into main May 21, 2026
8 checks passed
@CoreyRDean CoreyRDean deleted the fix/file-edit-grounding branch May 21, 2026 13:14
Copy link
Copy Markdown

@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: 08452d0fe8

ℹ️ 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".


var (
fileEditVerbPattern = regexp.MustCompile(`(?i)\b(modify|edit|update|rewrite|replace|append|prepend|insert|remove|delete|change)\b`)
pathTokenPattern = regexp.MustCompile(`(?:^|[\s"'` + "`" + `])((?:~|\.{1,2}|/)[^\s"'` + "`" + `,;:()]+|(?:\.[A-Za-z0-9_-]+)|(?:[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+))`)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Tighten target-token parsing to avoid non-file edit prompts

The file-target regex treats any dotted token as a path, so prompts like “update Node to 20.11.1” or “update to v1.2” will be classified as explicit file edits and trigger read_file repair/refusal even when no file path was requested. Because needsFileGrounding runs on every command/script response, this can block valid non-file workflows that include common version strings.

Useful? React with 👍 / 👎.

Comment on lines +83 to +87
if err := json.Unmarshal(call.Arguments, &args); err != nil || strings.TrimSpace(args.Path) == "" {
continue
}
for _, target := range targets {
if targetMatchesReadPath(target, args.Path, cwd, home) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require successful read_file before marking a target grounded

A target is considered grounded solely from read_file call arguments, without checking whether the tool actually returned file contents. If read_file is called with an unreadable/missing path (permission error, bad resolution, etc.), the grounding check still passes and the model may emit a mutating script despite never seeing real file contents, which breaks the fail-closed guarantee described in this change.

Useful? React with 👍 / 👎.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant