Compare commits

...
1 Commits
Author SHA1 Message Date
jeffryandClaude Opus 5 d7b169b87e 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
2026-09-08 12:02:44 -04:00
3 changed files with 70 additions and 3 deletions
+31
View File
@@ -1110,3 +1110,34 @@ opaque strings cannot support.*
**Belief that could be shown wrong:** *that fifty is readable.* **It is today.**
*The next remedy is not another compaction — it is the guide `bedrock` needed,
and it is not needed yet.*
## 2026-09-08 — a `404` at adoption has a third reading, and it is resolvable
**`externals` names two readings of a `404` — withdrawn, or access lost — and
they 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: `loomctl external add jeffry/homelab-cluster database` reported the
ambiguity for what was a missing `s`.*
**The tool was reporting an ambiguity it had the means to resolve.** *For the
by-name form it knows the repository, so on a `404` it now lists the published
surface:*
```
loomctl: jeffry/homelab-cluster publishes no "database.md". It publishes:
databases.md gitea.md private-access.md ...
```
**And it only claims that when the listing succeeds.** *If listing fails too, the
repository itself is unreachable and the original ambiguity is the honest
answer* — **the same discipline as reporting `public` and `not-public` rather than
`private`: say the thing you verified.**
**Not a gap against `externals`.** *The third reading cannot occur where that
document is speaking, which is `check` against a lock.* **It appears only at
adoption, which is ours.**
**Belief that could be shown wrong:** *that an extra round trip on a failure is
free.* **It is one partial clone on a path nobody takes twice**, *and if adoption
against large repositories becomes common the listing should be bounded.*
+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.
+14 -2
View File
@@ -253,6 +253,19 @@ func warnIfNotPublic(root, raw, vis string, out io.Writer) {
fmt.Fprintf(out, " record only the dependency, which loomctl cannot do yet.\n")
}
// NotFoundError is a 404, which over HTTP carries more than one reading.
//
// The convention names two — withdrawn, or access lost — because those are the
// two a locked document can have. Adoption by name has a third: a document that
// was never there under that name. Callers that know the repository can tell
// them apart; this type is how they get the chance.
type NotFoundError struct{ URL string }
func (e *NotFoundError) Error() string {
return fmt.Sprintf("404 unresolved: %s was withdrawn, or this credential cannot see it — "+
"over HTTP these are the same response", e.URL)
}
type fetched struct {
data []byte
url string
@@ -281,8 +294,7 @@ func fetch(cfg *config.Config, raw, ifNoneMatch string) (*fetched, string, error
}
return &fetched{data: b, url: resp.Request.URL.String()}, resp.Header.Get("ETag"), nil
case http.StatusNotFound:
return nil, "", fmt.Errorf("404 unresolved: %s was withdrawn, or this credential cannot see it — "+
"over HTTP these are the same response", raw)
return nil, "", &NotFoundError{URL: raw}
default:
return nil, "", fmt.Errorf("%s: %s", resp.Status, raw)
}