Files
externals/.loom/published/externals.md
T
jeffryandClaude Opus 5 101b0280c4 venues moved to bedrock; keep only the sentence about externals
bedrock publishes venues.md now, so the four-line section here had become the
second saying of a rule owned elsewhere.

Kept the one sentence that is genuinely this document's: put a choice where
reconciliation will look for it, because a .usages.md is what somebody opens when
a document moves.

The old heading — "venues, for things you cannot fetch" — was wrong and had been
since it was written. A venue is not defined by being unfetchable, it is defined
by your having decided something. Kafka's documentation is extremely fetchable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 10:55:32 -04:00

173 lines
7.9 KiB
Markdown

# 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.*~~
> **This was false and it was load-bearing.** *A stored path is short enough to
> read and therefore too short to resolve: it drops the host's routing, the
> branch, and — worst — the `published/` segment, **which is the whole contract.***
**The path is for a person. The origin is recorded in the lock**, resolved: host,
route, branch, and full path.
*Record the **resolved** URL and not the short form. A host may redirect a short
form to whatever the default branch is **at the time you ask** — so a lock holding
one is locked to a moving target, and a rename of the branch reports as a change
in the document.*
**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`.***
### Confidentiality does not travel with the copy
**Adopting is copying.** *So a document from a repository somebody may not read
ends up in a repository they may* — **and the publisher loses control of it at the
moment of adoption**, because the copy's visibility is governed by your repository
and not by theirs.
> **Do not adopt from a source less readable than the repository you are adopting
> into.** *If you may read it and your readers may not, copying it publishes it.*
**Two ways out, and the second is better when it is available.**
**Reference-only***record the lock and fetch on demand, keep no copy.* **You
give up reading it offline**, which is most of what a copy is for, *and you keep
the dependency recorded and checkable.*
**Ask them to publish** — *the thing you needed was almost certainly not the
confidential part.* **A repository that must stay private can still have a public
sibling that publishes**, and the split is usually along a line that already
exists: **the operational tree is what is sensitive; the pages telling somebody
what to decide are not.**
## Two facets beside it
- **`.usages.md`** — *what we use, and **which of our artifacts depend on it***
- **`.gaps.md`** — *what 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.*
## What a lock holds
**One record per adopted document, in `.loom/externals/.locks`:**
- **where it was fetched from** — *resolved, as above*
- **the publisher's `ETag`** — *verbatim*
- **the visibility the source could be read at, when it was fetched**
**It is committed**, because the thing it locks is committed, and *a lock that
travels separately from what it locks is the drift this is meant to prevent.*
> **A document with no lock is not broken; it is unlocked.** *Report it and fetch
> again.* **Do not adopt whatever the remote currently serves as the lock** — that
> asserts your copy is the one being served, which is the thing you were going to
> check.
### Why visibility is in the lock and not left to the moment
**Access is checked once and the copy is durable.** *A token gets you the read;
nothing revisits it afterwards.* **So the adoption's legitimacy rests on the
relative visibility of two repositories — and that is changeable by a checkbox, a
year later, by somebody who never saw the adoption.**
> **Record `public` or `not-public`. Never `private`.** *An anonymous request can
> tell you a repository is not public. It cannot tell two repositories private to
> **different people** apart* — **which is exactly the case where private into
> private genuinely widens access.** *Record the word you can verify.*
**Checking it costs one request per run, not one per document**, *because the only
thing that must be current is **your own** visibility.* **Re-check a source only
when the alarm would fire** — *you are public, it was recorded not-public* — *and
if it has since gone public, update the lock and say nothing.*
*A stored visibility decays in both directions, which is why the alarm re-checks
rather than trusting the record.*
## 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.*
> **A facet describes the local pair.** *A gap is true of **the copy you hold**,
> not of the document upstream — so a publisher fixing their end does not close
> it.* **It closes when you fetch the new copy and replace the pair**, and until
> then it is still true of what is in your tree.
**What survives a closed gap is not the gap. It is what the gap justified.** *If
you recorded a local workaround, ask whether it is retired or merely no longer
provisional* — **the second is the common case and it is invisible in the code**,
which is why it is an entry in your own log. *Somebody inheriting your workaround
will go looking for the gap that justified it, and find nothing.*
*A closed gap is **not** a decline. A decline is what you considered and did not
do; a closed gap is what you needed and got.*
**The new copy replaces the old pair wholesale.** *There is no merging a document
you do not own.*
## A choice about something you adopted does not go in a venue file
**Put a choice where reconciliation will look for it.** *When the thing you
decided about publishes a document you hold a copy of, the choice belongs in the
`.usages.md` beside that copy* — **because that is the file somebody opens when
the document moves.**
*Venues are for what is left: things that constrain you without publishing
anything you could adopt.* **See
[venues](https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/venues.md).**
## 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.