fix: round-9 review findings — pull errors, efficiency, config, Esc - #32
Merged
Merged
Conversation
Address findings from the pre-merge code review for round-9 fixes. image/efficiency.go - Admit hardlinks at every DiffType into the per-snapshot index. The previous round-9 fix only admitted Modified and Removed, so a path that was whiteout-deleted and then reintroduced as a hardlink (DiffType=Added per Stack) silently dropped the prior real-file bytes from the waste total — exactly the regression class the round-9 fix claimed to close. - Count only byte-contributing occurrences toward LayerCount. The size=0 hardlink replacement extends the run so the prior real occurrence is charged, but the user-facing 'across N layers' count must reflect layers that shipped real bytes. - Rewrite the indexTree comment to describe what the code actually does (extend the run, not terminate it). image/docker.go - Empty-errorDetail fallback now includes Code and Status. Registries that emit terse error events (Code=401 with empty Message, or just a Status='Retrying in N seconds') previously surfaced as a generic 'pull failed (registry returned an empty error)' with no diagnostic. - progress==nil callers (CI, JSON export) now skip per-event layer bookkeeping. Pre-round-9 those callers used io.Copy(io.Discard); after the round-9 unification they were paying for map updates whose output was immediately discarded. config/config.go - Replace the goccy-specific io.EOF guard with a TrimSpace+comment-strip pre-check. Library-agnostic, handles the truncated-document edge case (where goccy might return EOF after a partial decode and silently drop user values), and survives goccy version bumps. tui/model.go + README.md - Loading screen now renders 'Press q or Esc to exit' so the new round-9 Esc-state-machine comment and CHANGELOG bullet stop lying. Previously only the error screen rendered the hint. - README TUI keybindings table updated to describe Esc as dismiss-only, matching the Breaking change announced in CHANGELOG. Test rigor (CLAUDE.md PR-discipline rule #3 — every contract has a test) - TestEfficiency_RegularFileReplacedByHardlink_IsWasted now pins Score (0.5) and LayerCount (1) so future regressions in walkLiveFiles or the LayerCount semantics break this test. - TestEscapeQuitsInStateLoading/InStateError now assert m.fetchCtx.Err() == context.Canceled, pinning the quit-cancels-everything contract documented at model.go cancelInflight.
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.
Address findings from the pre-merge code review for the round-9 fix branch.
What changed
image/efficiency.go
indexTreenow admits hardlinks at everyDiffType. The previous round-9 fix only admittedModifiedandRemoved, so a path that was whiteout-deleted and then reintroduced as a hardlink (whichStackemits asDiffType=Added) silently dropped the prior real-file bytes from the waste total — exactly the regression class the round-9 fix claimed to close, just for theAddedvariant.LayerCountnow counts only byte-contributing occurrences. The size=0 hardlink replacement extends the run so the prior real occurrence is charged, but the user-facing 'across N layers' count must reflect layers that actually shipped real bytes.image/docker.go
CodeandStatus. Registries that emit terse error events (Code=401 with empty Message, or just a Status='Retrying in N seconds') previously surfaced as a genericpull failed (registry returned an empty error)with no diagnostic.progress==nilcallers (CI, JSON export) now skip per-event layer bookkeeping. Pre-round-9 those callers usedio.Copy(io.Discard); after the round-9 unification they were paying for map updates whose output was immediately discarded.config/config.go
io.EOFguard with aTrimSpace+comment-strip pre-check via a newhasYAMLContenthelper. Library-agnostic, handles the truncated-document edge case (where goccy might return EOF after a partial decode and silently drop user values), and survives goccy version bumps.tui/model.go + README.md
Press q or Esc to exitso the round-9 Esc-state-machine comment and CHANGELOG bullet stop describing a hint that wasn't there. Previously only the error screen rendered the hint.Test rigor (CLAUDE.md PR-discipline rule #3)
TestEfficiency_RegularFileReplacedByHardlink_IsWastednow pinsScore(0.5) andLayerCount(1) so future regressions inwalkLiveFilesor theLayerCountsemantics break the test.TestEscapeQuitsInStateLoading/InStateErrornow assertm.fetchCtx.Err() == context.Canceled, pinning the quit-cancels-everything contract documented atmodel.gocancelInflight.Deferred
The reviewer flagged a few larger architectural concerns that are intentionally not part of this PR (per CLAUDE.md rule #4 — no drive-by changes):
streamPullProgresswith moby'sStream.Wait(preserves errdefs typing).errorDetailreturn to tolerate transient daemon retries.Esccascade so the stateReady gate sits at the top rather than the bottom.walkFilesto admit hardlinks so hardlink-only-in-input paths are reachable.Each is a feature-sized change deserving its own PR.
Verification
go build ./...cleango vet ./...cleangopls checkon touched files clean🤖 Generated with Claude Code