Canonicalize bundle reason: sort consumers and dedupe files - #170
Merged
Conversation
The informational `reason` map's shape depended on how it was produced: a fresh withReason() stamp kept the file list in scan-traversal (or CLI arg) order, so any change to the import graph reshuffled the committed bundle's `reason` arrays; merges appended new consumers instead of sorting them; and only state's #bundleReason emitted a canonical form, so rewrites through a different path reordered what the previous one wrote. Make mergeReason always return the canonical form -- consumers sorted, each file list deduped and path-sorted, matching #bundleReason -- and have serialize() canonicalize `reason` like every other field instead of emitting it verbatim, so a parsed artifact's stale order can't leak back into the bytes. The union now accumulates in a Map (a plain-object '__proto__' consumer key would hit the prototype) and drops a non-array list, since parse leaves the informational field unvalidated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SDjoKAkSVQ4cfurL6RpuB1
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
This change ensures that bundle reason metadata is emitted in a canonical form—with consumers sorted alphabetically and file lists deduplicated and path-sorted—preventing the serialized artifact bytes from depending on merge history or discovery order.
Key Changes
mergeReason()function: Refactored to produce canonical output by:__proto__)undefinedwhen both inputs areundefinedBundle.serialize(): Now canonicalizes the reason field by passing it throughmergeReason(), ensuring that parsed artifacts don't leak stale record order into the serialized bytesTest coverage: Added three new tests validating:
Bundle.merge()emits reason consumers in sorted order, not merge orderBundle.withReason()emits path-sorted file lists, not discovery orderBundle.serialize()canonicalizes reason with sorted consumers and deduplicated, sorted filesComment clarification in
add.js: Updated to reflect that sorting is for deterministic diagnostics, with canonicalization handled byserialize()Implementation Details
The
mergeReason()function now usesMapto accumulate consumer→file-set mappings, then converts back to a plain object with sorted keys and deduplicated, sorted file arrays. This ensures consistent output regardless of input order or whether values come from a fresh stamp, a merge, or a parsed artifact.https://claude.ai/code/session_01SDjoKAkSVQ4cfurL6RpuB1