Files
externals/.loom/published/externals.md
T
jeffryandClaude Opus 5 c226760cae 404 is two answers wearing one status
Filed as a gap against this document by loom-cli's builder, and correctly: the
table listed 304, 200 and 410 and not 404, while gitea returns 404 for both "the
document was withdrawn" and "you no longer have access".

They are indistinguishable over HTTP by design — a host that distinguished them
would leak the existence of things you may not see — so the convention says
report both readings rather than pick one. They want different actions, re-pull
elsewhere versus ask for access, and a tool that guesses will be wrong half the
time silently.

Notes that ssh does distinguish them, permission denied against repository not
found, so a client with both transports should say which it used.

The claim was filed using this document's own test — could you say whose job it
is — which is the first time the convention has been used against the document
that defines it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 13:06:19 -04:00

3.4 KiB

Externals

How a repository records what it depends on, and finds out when it changes.

Pull what you use

You fetch a copy of somebody's document and keep it at .loom/externals/<host>/<path>.md. The path says where it came from, so nothing has to record an origin.

Pull what you need to understand, not everything it depends on. A document you fetch may refer to others; follow one when you hit something you do not know. Pre-resolving that is how you get a node_modules.

Two facets beside it

  • .usages.mdwhat we use, and which of our artifacts depend on it
  • .gaps.mdwhat we expected here and did not find

A usage that does not name what it justifies is half a usage. "We use the tailnet approach" is weak. "We use the tailnet approach — see manifests/ingress.yaml" is the whole value, because it answers the only question reconciliation asks.

Freshness is a conditional request

Locked on the publisher's ETag, verbatim — never a hash you compute. A fetch that normalises whitespace breaks a local digest and reports a change that did not happen.

304 nothing changed
200 changed — the new copy is a candidate, not a replacement
410 gone — follow whatever the response points at
404 unresolved. Report both readings; do not pick one

404 is two different answers wearing one status. The document was withdrawn, or you no longer have access — and over HTTP they are indistinguishable, because a host that distinguished them would leak the existence of things you may not see.

Say both. Do not guess. They want different actions — re-pull elsewhere, versus ask somebody for access — and a tool that picks one will be wrong half the time silently.

Over ssh they are distinguishable — permission denied against repository not found — so a client that has both transports should say which it used.

Reconciliation runs the other way

You cannot fold your facets back into their document. So the question is not what do we rewrite here:

Given what changed in theirs, what do we change in ours?

The facets usually survive unchanged. What moves is the manifests, the config, the code that a usage named — which is why a usage names them.

And gaps reconcile too, which is the half nobody builds for: a new version may have filled one, and nothing will tell you.

The new copy replaces the old pair wholesale. There is no merging a document you do not own.

Venues, for things you cannot fetch

.loom/venues/<name>.mdKafka, React, HTTP. Choices we made about something with no document to hang them on.

If there is a document, hang it there instead: put a choice where reconciliation will look for it.

Gaps that belong to nobody

.loom/gaps/a need with no owner. The test: could you say whose job this is? If yes, it is a claim against them and belongs in their .gaps.md.

Moving a file from gaps/ into an external's .gaps.md is how a gap gets homed. The position is the claim.

A gap records the local workaround beside the need. "We are using Docker Hub for now." Forgetting it was provisional is the disease; the provisional answer is fine — and when the gap is finally homed, every workaround that should be retired is findable.