Files
loom-cli/.loom/cart/current/specimens/proposed-spec.md
T
jeffryandClaude Opus 5 81c4917b9f a round, four externals, and the spec as a specimen
The repository is furnished rather than scaffolded — every file has content and a
reason, which is the distinction the spec itself draws when it says to resist
building init as a scaffolder.

Four externals fetched and locked under .loom/externals/: bedrock, externals,
annotating, cart. Copies, not to be edited, with a usages file beside cart
casting three of its four roles and leaving one open. The one left open is what
isolates work in progress, and the usages file says how we cast it wrong once and
why the belief was more useful than a preference would have been.

The spec moves out of the README and into the cart as a specimen, which makes it
discard-by-default — that is a stronger statement of "argue with this" than a
sentence asking them to. bedrock and externals stay non-discardable, since
accommodating them is what makes this a loom tool.

The daily says where we expect to be wrong: whether check can say anything useful
about a 404, and whether hash-as-lock survives a fetch that normalises anything,
which we have only tested against gitea.

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

3.1 KiB

loom-cli

Not built yet. This is the spec, written while it was fresh.

A small tool for the operations a person should not do by hand: fetch a document you depend on, and find out when it changed.


The one idea that makes it small

On gitea, a raw file's ETag is the git blob hash of that file. Verified on both a public and a private repository — the header and git rev-parse return the same value.

So there is nothing to record. git hash-object <the local copy> is the lock. Compare it to the remote's ETag and you have your answer.

No lock file, no state, no pull metadata to drift. And it catches a case we had not considered: if somebody edits the local copy, the hash stops matching and check reports it — which is correct, because an adopted copy that has been edited is no longer a copy of anything.

Commands

loom check

For every file under .loom/externals/: compute its hash, HEAD its source, compare.

result means
hashes match nothing changed
hashes differ upstream moved — the new copy is a candidate, not a replacement
local hash differs from what the remote had at last fetch somebody edited a copy that is not ours
404 gone, or access lost — these are indistinguishable over HTTP. Say so; do not pick one.

Report. Do not fix. A changed external becomes a polad in the cart, and somebody decides.

loom pull <url>

Fetch one document into .loom/externals/<host>/<path>. The path mirrors the URL, so nothing has to record an origin.

Do not create a .usages.md. An empty facet asserts that we have something to say and we do not — a file that carries no delta should not exist.

loom publish <path>

Verify a document in .loom/published/ is fetchable by somebody who is not you. One anonymous request.

Publishing is not an act you can complete alone. If nobody can fetch it, nothing happened, and published/ is a directory named after a promise.

loom initresist this

Its temptation is to scaffold, and four empty directories assert four things you have not decided. The useful version creates .loom/README.md and asks one question. Creating the tree is not a feature.

Credentials

Read over HTTP with a read-only token. Write over ssh. Split by operation, not by whether a repository is public.

Name the token for its scope — loom-read. A token cannot be asked what it may do: /api/v1/repos/… reports the account's rights, not the token's. Scope is set once at creation and invisible afterwards, so the name is the only record.

Token in ~, never in a repository.


Why so little

Every act in the model is a file in somebody's repository. Publishing is writing one; adopting is fetching a URL; reporting a gap is writing one; homing it is an mv. Nothing sends a service a request.

So this is a fetcher and a comparator, and it should stay one. If it starts holding state, ask what it knows that no repository knows — that is the thing that will rot.