fix: canonicalize the upstream key, not just the repository path [patch] - #39
Merged
Merged
Conversation
BranchStateHandler.Resolve built its MirrorKey from the caller's literal
upstream segment while canonicalizing only the sibling repository path:
MirrorKey key = new(route.Upstream, Canonicalize(route.RepositoryPath));
IUpstreamRegistry.TryResolve and IRepositoryAllowList.IsAllowed both key
their dictionaries with StringComparer.OrdinalIgnoreCase, so "GitHub" and
"github" pass as one configured upstream. Everything downstream of that
compares ordinally, so the two spellings produced two bare mirrors of the
same repository, two credential probes, and two diffs — exactly the
duplication issue #24 removed for repository-path casing, reachable
through the other half of the key.
Both halves now go through Canonicalize. Nothing sent to the forge
changes: the upstream base URL comes from configuration, not from the
caller's segment, and the repository URL is still built from the caller's
spelling of the path.
Fixes #37
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VSTy8Ye7JXqeFqvhrmnRt
|
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.



Fixes #37.
BranchStateHandler.Resolvecanonicalized one half of theMirrorKeyand passed the other through verbatim:route.Upstreamcomes straight off the URL segment. Both checks immediately above it key their dictionaries withStringComparer.OrdinalIgnoreCase— verified inUpstreamRegistryandRepositoryAllowList— soGitHubandgithubare accepted as the same configured upstream. Everything derived from the key afterwards compares ordinally.The result is the duplication issue #24 removed for repository-path casing, still reachable through the sibling field. Measured, not assumed: with the fix reverted,
TwoSpellingsOfOneUpstream_ShareOneMirrorFetchDiffAndAdmissionreportsclonerunning 2 times where it expects 1, and a secondmirror.gitappears on the volume.The change
One line — both halves of the key now go through
Canonicalize:Canonicalize's doc comment is rewritten to match: it had spelled out at length exactly why the path must be canonicalized while describing only the path, which is what made the omission easy to miss. It now names the upstream key as the other half and says why the same argument covers it.Nothing reaching the forge changes
Worth stating explicitly, because canonicalizing something a caller sent could look like rewriting a request:
registry.TryResolve, i.e. from configuration — never from the caller's segment. The canonical key is only this service's own bookkeeping.UpstreamUrl.TryCombine(upstreamBase, route.RepositoryPath, …)— the caller's spelling of the path, untouched, per the existing note that the forge is the authority on how it spells its own names.registry.TryResolveandallowList.IsAllowedstill receiveroute.Upstream, andEndpointLog.UnknownUpstreamstill logs the literal spelling the caller used.What the canonical key now feeds consistently:
MirrorStore's on-disk directory,MirrorKey.ToFlightKey()forSingleFlightcoalescing,DiffKey, the admission key, and the metric labels.Tests
Added to
RepositoryIdentityTests, the class #24's fix created, mirroring its four cases with upstream casing varying instead of path casing. Verified by stashing theBranchStateHandler.cschange and re-running: three of the four fail against the old implementation.TwoSpellingsOfOneUpstream_ShareOneMirrorFetchDiffAndAdmissionclonecount is 2, expected 1TwoSpellingsOfOneUpstream_ProduceOneMirrorDirectorymirror.gitdirectories on the volumeARequestWithTheUpstreamSpelledDifferently_MirrorsUnderTheCanonicalUpstream<root>/github/…TwoSpellingsOfOneUpstream_AreBothServedThe class
<remarks>is widened from "the path" to "the key", since it is now documenting both fields.Full suite: 203 passed, 0 failed, 0 skipped. Whole solution builds with 0 warnings, 0 errors.
🤖 Generated with Claude Code
https://claude.ai/code/session_018VSTy8Ye7JXqeFqvhrmnRt
Generated by Claude Code