diff --git a/go.mod b/go.mod
index 0e54e16..27ec03d 100644
--- a/go.mod
+++ b/go.mod
@@ -2,4 +2,4 @@
 
 go 1.25.0
 
-require golang.org/x/tools v0.44.0 // tagx:ignore
+require golang.org/x/tools v0.47.0 // tagx:ignore
diff --git a/go.sum b/go.sum
index d9db7e2..e2cccbb 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,2 @@
-golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
-golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
+golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
+golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
diff --git a/modfile/read.go b/modfile/read.go
index 5b528c7..9e35e1a 100644
--- a/modfile/read.go
+++ b/modfile/read.go
@@ -924,7 +924,7 @@
 	moduleStr  = []byte("module")
 )
 
-// ModulePath returns the module path from the gomod file text.
+// ModulePath returns the module path from the go.mod file text.
 // If it cannot find a module path, it returns an empty string.
 // It is tolerant of unrelated problems in the go.mod file.
 func ModulePath(mod []byte) string {
diff --git a/modfile/rule.go b/modfile/rule.go
index 9ab203b..20ba825 100644
--- a/modfile/rule.go
+++ b/modfile/rule.go
@@ -1477,7 +1477,7 @@
 	// Delete requirements we don't want anymore.
 	// Update versions and indirect comments on requirements we want to keep.
 	// If a requirement is in last{Direct,Indirect}Block with the wrong
-	// indirect marking after this, or if the requirement is in an single
+	// indirect marking after this, or if the requirement is in a single
 	// uncommented mixed block (oneFlatUncommentedBlock), move it to the
 	// correct block.
 	//
@@ -1537,7 +1537,7 @@
 	return nil
 }
 
-// AddExclude adds a exclude statement to the mod file. Errors if the provided
+// AddExclude adds an exclude statement to the mod file. Errors if the provided
 // version is not a canonical version string
 func (f *File) AddExclude(path, vers string) error {
 	if err := checkCanonicalVersion(path, vers); err != nil {
@@ -1708,7 +1708,7 @@
 	return nil
 }
 
-// DropIgnore removes a ignore directive with the given path.
+// DropIgnore removes an ignore directive with the given path.
 // It does nothing if no such ignore directive exists.
 func (f *File) DropIgnore(path string) error {
 	for _, t := range f.Ignore {
diff --git a/sumdb/client.go b/sumdb/client.go
index f926eda..47533a8 100644
--- a/sumdb/client.go
+++ b/sumdb/client.go
@@ -460,7 +460,7 @@
 	// on the continued availability of the misbehaving server.
 	// Preparing this data only reuses the tiled hashes needed for
 	// tlog.TreeHash(older.N, thr) above, so assuming thr is caching tiles,
-	// there are no new access to the server here, and these operations cannot fail.
+	// there are no new accesses to the server here, and these operations cannot fail.
 	fmt.Fprintf(&buf, "proof of misbehavior:\n\t%v", h)
 	if p, err := tlog.ProveTree(newer.N, older.N, thr); err != nil {
 		fmt.Fprintf(&buf, "\tinternal error: %v\n", err)
diff --git a/sumdb/note/note.go b/sumdb/note/note.go
index c95777f..a41a0e5 100644
--- a/sumdb/note/note.go
+++ b/sumdb/note/note.go
@@ -238,7 +238,7 @@
 	return name != "" && utf8.ValidString(name) && strings.IndexFunc(name, unicode.IsSpace) < 0 && !strings.Contains(name, "+")
 }
 
-// NewVerifier construct a new [Verifier] from an encoded verifier key.
+// NewVerifier constructs a new [Verifier] from an encoded verifier key.
 func NewVerifier(vkey string) (Verifier, error) {
 	name, vkey, _ := strings.Cut(vkey, "+")
 	hash16, key64, _ := strings.Cut(vkey, "+")
diff --git a/sumdb/note/note_test.go b/sumdb/note/note_test.go
index 22267e7..f5028f6 100644
--- a/sumdb/note/note_test.go
+++ b/sumdb/note/note_test.go
@@ -127,7 +127,7 @@
 
 	testSignerAndVerifier(t, Name, signer, verifier)
 
-	// Check that GenerateKey returns error from rand reader.
+	// Check that GenerateKey returns an error from rand reader.
 	_, _, err = GenerateKey(iotest.TimeoutReader(iotest.OneByteReader(rand.Reader)), Name)
 	if err == nil {
 		t.Fatalf("GenerateKey succeeded with error-returning rand reader")
diff --git a/sumdb/storage/storage.go b/sumdb/storage/storage.go
index ed5df49..fb343f2 100644
--- a/sumdb/storage/storage.go
+++ b/sumdb/storage/storage.go
@@ -11,7 +11,7 @@
 type Storage interface {
 	// ReadOnly runs f in a read-only transaction.
 	// It is equivalent to ReadWrite except that the
-	// transaction's BufferWrite method will fail unconditionally.
+	// transaction's BufferWrites method will fail unconditionally.
 	// (The implementation may be able to optimize the
 	// transaction if it knows at the start that no writes will happen.)
 	ReadOnly(ctx context.Context, f func(context.Context, Transaction) error) error
@@ -30,7 +30,7 @@
 // as executed by [Storage]'s ReadOnly or ReadWrite methods.
 type Transaction interface {
 	// ReadValue reads the value associated with a single key.
-	// If there is no value associated with that key, ReadKey returns an empty value.
+	// If there is no value associated with that key, ReadValue returns an empty value.
 	// An error is only returned for problems accessing the storage.
 	ReadValue(ctx context.Context, key string) (value string, err error)
 
diff --git a/sumdb/tlog/tile.go b/sumdb/tlog/tile.go
index 37771c5..153f41c 100644
--- a/sumdb/tlog/tile.go
+++ b/sumdb/tlog/tile.go
@@ -22,7 +22,7 @@
 // of the form tile/H/L/NNN[.p/W].
 // The .p/W suffix is present only for partial tiles, meaning W < 2**H.
 // The NNN element is an encoding of N into 3-digit path elements.
-// All but the last path element begins with an "x".
+// All but the last path element begin with an "x".
 // For example,
 // Tile{H: 3, L: 4, N: 1234067, W: 1}'s path
 // is tile/3/4/x001/x234/067.p/1, and
diff --git a/sumdb/tlog/tlog.go b/sumdb/tlog/tlog.go
index 480b5ef..cbfca87 100644
--- a/sumdb/tlog/tlog.go
+++ b/sumdb/tlog/tlog.go
@@ -34,7 +34,7 @@
 	return []byte(`"` + h.String() + `"`), nil
 }
 
-// UnmarshalJSON unmarshals a hash from JSON string containing the a base64-encoded hash.
+// UnmarshalJSON unmarshals a hash from a JSON string containing a base64-encoded hash.
 func (h *Hash) UnmarshalJSON(data []byte) error {
 	if len(data) != 1+44+1 || data[0] != '"' || data[len(data)-2] != '=' || data[len(data)-1] != '"' {
 		return errors.New("cannot decode hash")
@@ -246,7 +246,7 @@
 // TreeHash computes the hash for the root of the tree with n records,
 // using the HashReader to obtain previously stored hashes
 // (those returned by StoredHashes during the writes of those n records).
-// TreeHash makes a single call to ReadHash requesting at most 1 + log₂ n hashes.
+// TreeHash makes a single call to ReadHashes requesting at most 1 + log₂ n hashes.
 func TreeHash(n int64, r HashReader) (Hash, error) {
 	if n == 0 {
 		return emptyHash, nil
diff --git a/zip/zip.go b/zip/zip.go
index 48363ce..ddab4c3 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -668,7 +668,7 @@
 	// techniques like git ls-files, but this approach most closely matches what
 	// the Go command does, which is beneficial.
 	//
-	// Note: some of this code copied from https://go.googlesource.com/go/+/refs/tags/go1.16.5/src/cmd/go/internal/modfetch/codehost/git.go#826.
+	// Note: some of this code is copied from https://go.googlesource.com/go/+/refs/tags/go1.16.5/src/cmd/go/internal/modfetch/codehost/git.go#826.
 	cmd := exec.Command("git", "-c", "core.autocrlf=input", "-c", "core.eol=lf", "archive", "--format=zip", rev)
 	if subdir != "" {
 		cmd.Args = append(cmd.Args, subdir)
@@ -710,7 +710,7 @@
 	}
 
 	if !haveLICENSE && subdir != "" {
-		// Note: this method of extracting the license from the root copied from
+		// Note: this method of extracting the license from the root is copied from
 		// https://go.googlesource.com/go/+/refs/tags/go1.20.4/src/cmd/go/internal/modfetch/coderepo.go#1118
 		// https://go.googlesource.com/go/+/refs/tags/go1.20.4/src/cmd/go/internal/modfetch/codehost/git.go#657
 		cmd := exec.Command("git", "cat-file", "blob", rev+":LICENSE")
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 52757ce..e43f70e 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -1617,7 +1617,7 @@
 			desc:    "from subdir",
 			version: module.Version{Path: "example.com/foo/bar", Version: "v0.0.1"},
 			subdir:  "d/",
-			// Note: File paths are zipped as if the subdir were the root. ie d.go instead of d/d.go.
+			// Note: File paths are zipped as if the subdir were the root, i.e., d.go instead of d/d.go.
 			// subdirs without a license hoist the license from the root
 			wantFiles: []string{"d.go", "LICENSE"},
 			wantData:  map[string]string{"LICENSE": "root license\n"},
@@ -1626,7 +1626,7 @@
 			desc:    "from subdir with license",
 			version: module.Version{Path: "example.com/foo/bar", Version: "v0.0.1"},
 			subdir:  "e/",
-			// Note: File paths are zipped as if the subdir were the root. ie e.go instead of e/e.go.
+			// Note: File paths are zipped as if the subdir were the root, i.e., e.go instead of e/e.go.
 			// subdirs with a license use their own
 			wantFiles: []string{"LICENSE", "e.go"},
 			wantData:  map[string]string{"LICENSE": "e license\n"},
@@ -1635,7 +1635,7 @@
 			desc:    "from submodule subdir",
 			version: module.Version{Path: "example.com/foo/bar/f", Version: "v0.0.1"},
 			subdir:  "f/",
-			// Note: File paths are zipped as if the subdir were the root. ie f.go instead of f/f.go.
+			// Note: File paths are zipped as if the subdir were the root, i.e., f.go instead of f/f.go.
 			// subdirs without a license hoist the license from the root
 			wantFiles: []string{"go.mod", "f.go", "LICENSE"},
 			wantData:  map[string]string{"LICENSE": "root license\n"},