Not built. Recorded now because the reasoning is a day old and would be expensive to reconstruct. The idea that makes it small: on gitea a raw file's ETag is the git blob hash, verified on both a public and a private repository. So there is nothing to record — git hash-object on the local copy is the lock, and comparing it to the remote's ETag is the whole mechanism. No lock file and no state to drift. It also catches a case we had not considered: an edited local copy stops matching, which is correct, because an adopted copy that has been edited is no longer a copy. check reports and does not fix; a changed external becomes a polad and somebody decides. pull fetches one document and deliberately does not create an empty facet. publish verifies the document is fetchable by somebody who is not you, because publishing is not an act you can complete alone. And init is flagged to resist: its temptation is to scaffold, and four empty directories assert four things nobody has decided. Credentials split by operation rather than by visibility, with the token named for its scope, since a token cannot be asked what it may do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
86 lines
3.1 KiB
Markdown
86 lines
3.1 KiB
Markdown
# 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 init` — **resist 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.*
|