diff --git a/.loom/externals/.locks b/.loom/externals/.locks index cf4920f..91eab3c 100644 --- a/.loom/externals/.locks +++ b/.loom/externals/.locks @@ -11,4 +11,4 @@ git.hypertheory-labs.dev/loom/bedrock/sibling-facets.md https://git.hypertheory- git.hypertheory-labs.dev/loom/bedrock/starting.md https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/starting.md "b9eefba0f4668a496ccfc6a1377277f0721456d2" public git.hypertheory-labs.dev/loom/cart/cart.md https://git.hypertheory-labs.dev/loom/cart/raw/branch/main/.loom/published/cart.md "15331f1a9cc81bf61a44830cfbb7c274f4c2b119" public git.hypertheory-labs.dev/loom/externals/externals.md https://git.hypertheory-labs.dev/loom/externals/raw/branch/main/.loom/published/externals.md "50673ccffc14d57150ac0a9027b0712d9dcf940d" -git.hypertheory-labs.dev/loom/loom-cli/guarantees.md https://git.hypertheory-labs.dev/loom/loom-cli/raw/branch/main/.loom/published/guarantees.md "432822b3f91ec7c7acd2c868490b7cdcde5db99a" public +git.hypertheory-labs.dev/loom/loom-cli/guarantees.md https://git.hypertheory-labs.dev/loom/loom-cli/raw/branch/main/.loom/published/guarantees.md "e8c697ea61df47838f38af7353e29d9ae3f7070c" diff --git a/.loom/externals/git.hypertheory-labs.dev/loom/loom-cli/guarantees.md b/.loom/externals/git.hypertheory-labs.dev/loom/loom-cli/guarantees.md index 432822b..e8c697e 100644 --- a/.loom/externals/git.hypertheory-labs.dev/loom/loom-cli/guarantees.md +++ b/.loom/externals/git.hypertheory-labs.dev/loom/loom-cli/guarantees.md @@ -88,6 +88,27 @@ has not earned.** **That anything is checked when you are not looking.** *Nothing here runs on a schedule, and a document nobody checks is a document nobody is checking.* +## What is promised about the things that are not promised + +**The command surface may change. This page will say when it does.** + +*A consumer whose README tells a person to type `loomctl external add loom/ +.md` depends on that command by name — **not by parsing it, which is why no +check would ever catch a rename**. The thing that moved would not be a document +they adopted.* + +> **So the exclusion is narrower than "you are on your own".** *We do not promise +> the surface holds; **we promise that a change to it lands here**, on a page you +> can lock* — **and a lock on this page reports `200` the day a command is +> renamed.** + +**That is the whole mechanism, and it costs one section:** + +### Surface changes + +*Append-only, newest last. Nothing yet — the commands have not been renamed since +this page existed.* + --- *Verified fetchable by somebody who is not us on 2026-09-08.* diff --git a/.loom/externals/git.hypertheory-labs.dev/loom/loom-cli/guarantees.usages.md b/.loom/externals/git.hypertheory-labs.dev/loom/loom-cli/guarantees.usages.md index a316de7..ca4070a 100644 --- a/.loom/externals/git.hypertheory-labs.dev/loom/loom-cli/guarantees.usages.md +++ b/.loom/externals/git.hypertheory-labs.dev/loom/loom-cli/guarantees.usages.md @@ -22,6 +22,15 @@ not parse them, but a person following our README will type them.* > check will catch it*, **because the thing that moved is not a document we > adopted.** -*Recorded rather than solved. It is the same class as the sub-page staleness limit -in `HANDOFF.md`: the lock watches documents, and some of what we lean on is not a -document.* +**Half-closed upstream, 2026-09-08.** *`guarantees.md` grew an append-only +`Surface changes` section.* **The promise is not that the surface holds — it is +that a change to it lands on the page we already lock**, *so a rename reports +`200` here the day it happens.* + +> **The record is not that the thing never changes. It is that the change is +> findable by whoever depended on it.** + +**The half still open is ours and cannot be closed by them:** *our README tells a +person to type a command, and a person typing is not a dependency any lock +watches.* **Same class as the sub-page staleness limit: the lock watches +documents, and some of what we lean on is not a document.** diff --git a/scripts/generate.mjs b/scripts/generate.mjs index 411c1df..abeacac 100644 --- a/scripts/generate.mjs +++ b/scripts/generate.mjs @@ -170,9 +170,14 @@ function guidePage(repo, records, existing) { // Starlight renders `banner` above the page. The reader sees the doubt even // if nobody has reconciled it yet — the site degrades honestly rather than // reading as true. + // Deliberately says nothing about which side is newer. An etag is opaque — + // that is the point of it — so two of them cannot be ordered, and this can + // only ever know that they differ. The first time it fired in real use, the + // guide was written against a version newer than the copy here, and a + // message claiming "an older X" was confidently backwards. const which = stale.map((s) => `${s}`).join(', ') data.banner = { - content: `Written against an older ${which}. The source has changed since — this page may no longer be true. Re-read it, then npm run ack.`, + content: `Written against a different ${which} than the copy in this repository — so one of the two is behind. Run loomctl external check, or re-read this page, then npm run ack.`, } } else { delete data.banner diff --git a/src/content/docs/loom-cli/guarantees.md b/src/content/docs/loom-cli/guarantees.md index 80e59b5..df6a724 100644 --- a/src/content/docs/loom-cli/guarantees.md +++ b/src/content/docs/loom-cli/guarantees.md @@ -5,8 +5,8 @@ loom: generated: true path: git.hypertheory-labs.dev/loom/loom-cli/guarantees.md source: https://git.hypertheory-labs.dev/loom/loom-cli/raw/branch/main/.loom/published/guarantees.md - etag: '"432822b3f91ec7c7acd2c868490b7cdcde5db99a"' - visibility: public + etag: '"e8c697ea61df47838f38af7353e29d9ae3f7070c"' + visibility: unknown --- **Not what it does — `--help` says that, and the commands will change.** *This is what will keep being true while they do.* @@ -96,6 +96,27 @@ has not earned.** **That anything is checked when you are not looking.** *Nothing here runs on a schedule, and a document nobody checks is a document nobody is checking.* +## What is promised about the things that are not promised + +**The command surface may change. This page will say when it does.** + +*A consumer whose README tells a person to type `loomctl external add loom/ +.md` depends on that command by name — **not by parsing it, which is why no +check would ever catch a rename**. The thing that moved would not be a document +they adopted.* + +> **So the exclusion is narrower than "you are on your own".** *We do not promise +> the surface holds; **we promise that a change to it lands here**, on a page you +> can lock* — **and a lock on this page reports `200` the day a command is +> renamed.** + +**That is the whole mechanism, and it costs one section:** + +### Surface changes + +*Append-only, newest last. Nothing yet — the commands have not been renamed since +this page existed.* + --- *Verified fetchable by somebody who is not us on 2026-09-08.* diff --git a/src/content/docs/loom-cli/index.mdx b/src/content/docs/loom-cli/index.mdx index f20f236..28507cd 100644 --- a/src/content/docs/loom-cli/index.mdx +++ b/src/content/docs/loom-cli/index.mdx @@ -3,17 +3,124 @@ title: loom-cli loom: writtenAgainst: - path: git.hypertheory-labs.dev/loom/loom-cli/guarantees.md - etag: '"432822b3f91ec7c7acd2c868490b7cdcde5db99a"' + etag: '"e8c697ea61df47838f38af7353e29d9ae3f7070c"' --- -This page is yours. Nothing overwrites it. -Put what a copy cannot carry here — a worked example, the order to read things -in, the thing that only makes sense once you have done it twice. +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. -Prefer an example to an explanation. An explanation is a second saying of a rule -that is owned on one of the pages beside this one, and it goes stale silently. An -example goes stale visibly, because the artifacts in it are the wrong shape. +The tool arrived after the documents it manages, which turned out to be the +useful way to meet it. -The pages in this section are copies of what `loom/loom-cli` publishes, and are -regenerated. When one of them moves, this page gets a banner asking whether it is -still true — the build cannot answer that, so it asks. +## 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: + +```console +$ loomctl external check +git.hypertheory-labs.dev/loom/annotating/annotating.md unlocked no origin recorded — `loomctl external add ` supplies it without rewriting this copy +git.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 + +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: + +```console +$ loomctl external add loom/bedrock starting.md +locked 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 + +Eight documents locked. Then, on a Sunday afternoon, this: + +```console +$ loomctl external check +git.hypertheory-labs.dev/loom/cart/cart.md same +git.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: + +```console +$ loomctl external apply +applied git.hypertheory-labs.dev/loom/externals/externals.md + check git.hypertheory-labs.dev/loom/externals/externals.usages.md names what depends on this +``` + +And 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 this +``` + +## 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 + +```console +$ 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 reads +``` + +The [guarantees](/loom-cli/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.