loom-cli
A fetcher and a comparator. It adopts a document you depend on, records where the copy came from, and later asks the publisher whether theirs has moved.
The tool arrived after the documents it manages, which turned out to be the useful way to meet it.
Eight documents, and a tool that would not guess
Section titled “Eight documents, and a tool that would not guess”loom-cli had adopted eight documents by hand, months of prose fetched with
curl into the right directory. The first thing the tool ever said about them
was this:
$ loomctl external checkgit.hypertheory-labs.dev/loom/annotating/annotating.md unlocked no origin recorded — `loomctl external add <url>` supplies it without rewriting this copygit.hypertheory-labs.dev/loom/bedrock/loom-directory.md unlocked no origin recorded — ...git.hypertheory-labs.dev/loom/bedrock/publication.md unlocked no origin recorded — ...git.hypertheory-labs.dev/loom/bedrock/starting.md unlocked no origin recorded — ...git.hypertheory-labs.dev/loom/cart/cart.md unlocked no origin recorded — ...git.hypertheory-labs.dev/loom/externals/externals.md unlocked no origin recorded — ...unlocked means I cannot tell you. There was an obvious thing to do instead
— fetch each document and adopt whatever etag came back — and the tool refuses
to, because that would assert the local copy is the one being served, which is
the exact thing it was about to check. On a copy somebody had edited, it would
have been wrong and silent.
That refusal is not visible as a feature. It is only visible as an artifact: six lines that decline to answer.
Supplying the origin, without an overwrite
Section titled “Supplying the origin, without an overwrite”The path does not round-trip — .../loom/bedrock/starting.md has dropped the
route, the branch, and the publisher’s .loom/published/ — so nothing but you
can say where a hand-fetched copy came from. Say it once:
$ loomctl external add loom/bedrock starting.mdlocked git.hypertheory-labs.dev/loom/bedrock/starting.md from https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/starting.md etag "7d997a30248a88c90b23f2f9453d7cfceb03848e" (bytes verified identical; nothing was rewritten)locked, not adopted. The bytes decide: identical, so the lock’s claim is
verified rather than assumed, and not one byte of the local copy is rewritten. If
they had differed, the copy would have been left exactly where it was, because a
copy that differs is the only evidence that something moved while nothing was
watching.
The first real answer
Section titled “The first real answer”Eight documents locked. Then, on a Sunday afternoon, this:
$ loomctl external checkgit.hypertheory-labs.dev/loom/cart/cart.md samegit.hypertheory-labs.dev/loom/externals/externals.md CHANGED staged as a polad; git.hypertheory-labs.dev/loom/externals/externals.usages.md names what to fix
1 staged in .loom/cart/current/polad — apply or discard; nothing here drifts into being kept.The document had gained a section that afternoon. Nobody had said so, and nobody in this repository would have found out.
Two things in that line are doing work. The new copy is staged, not applied — it is a candidate, and taking it is a separate act:
$ loomctl external applyapplied git.hypertheory-labs.dev/loom/externals/externals.md check git.hypertheory-labs.dev/loom/externals/externals.usages.md names what depends on thisAnd the staged copy carries the etag it was served with, so applying locks the bytes somebody actually read rather than whatever the publisher is serving by then.
The second is the pointer to the .usages.md. Reconciliation runs the other way:
the question is not what do we rewrite here, it is given what changed in
theirs, what do we change in ours — and the facets usually survive untouched.
What moves is the code a usage named. When there is no such file, the tool says
so, which is a finding rather than an omission:
CHANGED staged as a polad; no .usages.md — nothing records what depends on thisWhat it will not do
Section titled “What it will not do”It never writes over the network. It has no push, no publish, no token that needs write scope. Everything it writes is a file in your working tree, and committing is yours.
It does not repair. check reports; apply is a person taking a candidate one
document at a time.
It grants no access. Everything it does is possible with copy and paste — it is a mast, not a lock. What it adds over a paste is that the copy can answer where did this come from, and were we allowed to have it, which a pasted document cannot, because the evidence is gone rather than because the question is hard.
And it does not run when you are not looking. Nothing here is scheduled. A document nobody checks is a document nobody is checking.
Where to start
Section titled “Where to start”$ go build -o loomctl . && ./loomctl --help$ loomctl external list loom/bedrock # what a repository publishes$ loomctl external add loom/bedrock starting.md$ loomctl external check # safe anywhere; it only readsThe guarantees page is the contract: what will keep being true while the commands change, the lock file’s format, and — the part worth reading first if you are reimplementing this — what is deliberately not promised.