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>
173 lines
7.9 KiB
Markdown
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.
|