refactor(conflict)!: key path overlap by file or by directory - #575
Merged
Conversation
## Summary ### Why? `fileoverlap` serialized two batches only when they changed the exact same file. That is the narrowest useful reading of target overlap, and for a queue whose directories are tightly coupled it is too narrow: two changes to sibling files in one package can break each other without either one touching the path the other did, and the analyzer will happily speculate them in parallel. The right granularity is not a property of the analyzer. It follows from how tightly coupled a directory's contents are in the repository behind the queue, which is something only the integrator wiring that queue up can know. So it belongs at construction, next to the resolver, rather than baked into the package. ### What? Overlap is measured on a key projected from each changed path. `PathKey` is that projection, chosen at construction and applied to every path before the two batches' sets are intersected; `Analyze` is otherwise unchanged. Two projections ship with the package. `ByFile` keys on the whole path and reproduces the previous behaviour. `ByDirectory` keys on the immediate parent, so batches touching sibling files conflict too — strictly coarser, since every file overlap is also a directory overlap. It buys protection against semantic conflicts between neighbouring files and pays for it in parallelism, which is the trade the integrator is choosing between. Paths at the repository root key on `.` under `ByDirectory`, so a batch touching `README.md` conflicts with one touching `go.mod`. Root files are usually build configuration and usually do interact, so this is deliberate rather than incidental. The package is renamed `fileoverlap` → `pathoverlap`, because the unit of overlap is now a path-derived key rather than a file. `New` takes the key as a third argument and panics on nil, mirroring `heuristic.New`. `conflict.Analyzer`, `conflict.Config` and `ConflictTypeTargetOverlap` are untouched — a folder is a coarser target, not a different kind of one. The only caller, `file-overlap-queue` in the orchestrator profiles, passes `ByFile` and keeps its behaviour and its name. One incidental behaviour change: `ByFile` runs `path.Clean`, where paths were previously compared verbatim. A provider emitting an unclean path used to produce a missed conflict. ## Test Plan ✅ `make test` — 98 pass ✅ `make lint`, `make check-gazelle`, `make check-tidy` New coverage in `pathoverlap_test.go`: - `TestPathKey` — both projections over a nested path, a repository-root file, and an unclean path. - Sibling files in one directory: no conflict under `ByFile`, conflict under `ByDirectory`; files in sibling directories conflict under neither; the same file still conflicts under both. - Two root-level files conflict under `ByDirectory` while a nested file in the same batch set does not. - `New` panics when the key is nil.
behinddwalls
marked this pull request as ready for review
August 12, 2026 03:15
mnoah1
approved these changes
Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Why?
fileoverlapserialized two batches only when they changed the exact same file. That is the narrowest useful reading of target overlap, and for a queue whose directories are tightly coupled it is too narrow: two changes to sibling files in one package can break each other without either one touching the path the other did, and the analyzer will happily speculate them in parallel.The right granularity is not a property of the analyzer. It follows from how tightly coupled a directory's contents are in the repository behind the queue, which is something only the integrator wiring that queue up can know. So it belongs at construction, next to the resolver, rather than baked into the package.
What?
Overlap is measured on a key projected from each changed path.
PathKeyis that projection, chosen at construction and applied to every path before the two batches' sets are intersected;Analyzeis otherwise unchanged.Two projections ship with the package.
ByFilekeys on the whole path and reproduces the previous behaviour.ByDirectorykeys on the immediate parent, so batches touching sibling files conflict too — strictly coarser, since every file overlap is also a directory overlap. It buys protection against semantic conflicts between neighbouring files and pays for it in parallelism, which is the trade the integrator is choosing between.Paths at the repository root key on
.underByDirectory, so a batch touchingREADME.mdconflicts with one touchinggo.mod. Root files are usually build configuration and usually do interact, so this is deliberate rather than incidental.The package is renamed
fileoverlap→pathoverlap, because the unit of overlap is now a path-derived key rather than a file.Newtakes the key as a third argument and panics on nil, mirroringheuristic.New.conflict.Analyzer,conflict.ConfigandConflictTypeTargetOverlapare untouched — a folder is a coarser target, not a different kind of one. The only caller,file-overlap-queuein the orchestrator profiles, passesByFileand keeps its behaviour and its name.One incidental behaviour change:
ByFilerunspath.Clean, where paths were previously compared verbatim. A provider emitting an unclean path used to produce a missed conflict.Test Plan
✅
make test— 98 pass✅
make lint,make check-gazelle,make check-tidyNew coverage in
pathoverlap_test.go:TestPathKey— both projections over a nested path, a repository-root file, and an unclean path.ByFile, conflict underByDirectory; files in sibling directories conflict under neither; the same file still conflicts under both.ByDirectorywhile a nested file in the same batch set does not.Newpanics when the key is nil.