a 404 at adoption has a third reading, and the tool can resolve it

externals names two readings of a 404 — withdrawn, or access lost — and those are
the two a locked document can have. Adoption by name has a third: a document that
was never there under that name. Found in use, where a missing s reported the
ambiguity instead of the typo.

The tool was reporting an ambiguity it had the means to resolve: the by-name form
knows the repository, so on a 404 it now lists the published surface and says which
names exist. It claims that only when the listing succeeds — if listing fails too,
the repository is unreachable and the original ambiguity is the honest answer,
which is the same discipline as recording public and not-public rather than
private.

Not filed as a gap against externals. The third reading cannot occur where that
document is speaking, which is check against a lock; it exists only at adoption,
which is ours.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018UTxuSizozEA8yDitPuris
This commit is contained in:
2026-09-08 12:02:44 -04:00
co-authored by Claude Opus 5
parent a5093c1efc
commit d7b169b87e
3 changed files with 70 additions and 3 deletions
+25 -1
View File
@@ -2,6 +2,7 @@ package external
import (
"bytes"
"errors"
"fmt"
"io"
"os/exec"
@@ -50,7 +51,30 @@ func AddByName(root, ownerRepo, name string, out io.Writer) error {
}
raw = fmt.Sprintf(raw, branch)
}
return Add(root, raw, "", out)
err = Add(root, raw, "", out)
// A 404 here has a reading the convention does not list, because it can only
// happen at adoption: the name is wrong. We know the repository, so rather
// than reporting an ambiguity we can resolve, look.
var nf *NotFoundError
if errors.As(err, &nf) {
var names strings.Builder
if lerr := List(ctx.CloneURL(ownerRepo), &names); lerr == nil {
return fmt.Errorf("%s publishes no %q. It publishes:\n%s",
ownerRepo, name, indent(names.String()))
}
// Listing failed too, so the repository itself is unreachable and the
// original ambiguity stands.
}
return err
}
func indent(s string) string {
var b strings.Builder
for _, line := range strings.Split(strings.TrimRight(s, "\n"), "\n") {
fmt.Fprintf(&b, " %s\n", line)
}
return strings.TrimRight(b.String(), "\n")
}
// ListByName enumerates a repository named against the current context.