loomctl external: list, add, check — and its first run found two changed documents
Go, standard library only, with git shelled out for list alone. list enumerates a publisher's .loom/published by partial clone and ls-tree; add fetches one document, writes it under .loom/externals and records the resolved origin and the publisher's ETag in .loom/externals/.locks; check asks conditionally and reports. The first real run did what the tool exists for. All eight documents adopted by hand before it existed reported unlocked — the tool refuses to invent a lock by adopting whatever the remote currently serves, since that would assert the local copy is the one being served, which is the thing it was about to check. Locking them fetched two that had moved: bedrock/starting.md, which now says the worked example is private and will not link to something you cannot fetch, and cart.md, which went to v1. cart v1 changes a role we cast: a cart is not committed, because a committed cart grows a third file by itself — version control does not require anybody to ask, so the two-file rule is never invoked — and because ignored, gone means gone. Adds .loom/cart/ to .gitignore and supersedes the isolation entry rather than editing it. osprey and marmalade are already in history and are left there: rewriting to honour a rule adopted afterwards costs more than it buys. Records the conflict this creates rather than settling it: the annotation protocol here says commit before dissolving because git is the only archive, and an ignored cart has no archive, so dissolving would destroy the annotations outright. Credentials are read-only, per host, and passed to git through the environment rather than argv, because argv is visible to every process on the machine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018UTxuSizozEA8yDitPuris
This commit is contained in:
@@ -518,3 +518,55 @@ the cart that named it.*
|
||||
**Recorded rather than fixed.** *The role still works; its justification was
|
||||
written for a case that has not yet occurred.* **If a round ever does produce code
|
||||
while it is open, nothing here changes.**
|
||||
|
||||
## 2026-09-07 — `loomctl external` exists, and its first run reconciled two documents `marmalade`
|
||||
|
||||
**Built: `list`, `add`, `check`.** *Go, no dependencies outside the standard
|
||||
library, `git` shelled out for `list` only.*
|
||||
|
||||
**The first real run did the thing the tool is for.** *Eight documents were
|
||||
adopted by hand before it existed; `check` reported all eight `unlocked`, and
|
||||
`add` locked them* — **and two came back changed**, `bedrock/starting.md` *and*
|
||||
`cart/cart.md`. **Neither change would have been noticed by anybody.**
|
||||
|
||||
**Believed to advance:** *the lock is what makes `check` possible at all.* **With
|
||||
no lock there is nothing to compare and the only honest report is `unlocked`** —
|
||||
*and the tool refuses to invent one by adopting whatever the remote currently
|
||||
serves.*
|
||||
|
||||
**Belief that could be shown wrong:** *that a per-person path derived from the URL
|
||||
is good enough.* **`<host>/<owner>/<repo>/<name>.md` guesses that the first two
|
||||
path segments name an owner and a repository**, *which is true of gitea, GitHub and
|
||||
GitLab and is not a rule.* **`--path` exists for when it is wrong.**
|
||||
|
||||
## 2026-09-07 — supersedes the isolation role: a cart is not committed `marmalade`
|
||||
|
||||
**`cart` is now `v1` and it changed the thing we cast a role on.**
|
||||
|
||||
> **So the cart is not committed.** *It lives in the working tree of the machine
|
||||
> the two presences share, and `.loom/cart/` is ignored by version control.*
|
||||
|
||||
**The reason is not tidiness:** *a committed cart grows a third file by itself.*
|
||||
**The two-file rule defends against somebody asking for one; version control does
|
||||
not require anybody to ask** — *anyone who can clone can add a third, and the
|
||||
agreement's test is never invoked because nobody had the conversation.*
|
||||
|
||||
**And it is what makes a round end.** *Committed, a cart is gone from the tree and
|
||||
permanent in history* — **so "gone" means "no longer live" and the negotiation
|
||||
stays quotable forever.** *Ignored, gone means gone.*
|
||||
|
||||
**So `.loom/cart/` is now in `.gitignore`.** *The isolation role entry above
|
||||
assumed the cart lived on the default branch; **the cart lives on no branch.***
|
||||
*What survives of that entry is the other half: work is isolated on a branch named
|
||||
for the round that authorised it.*
|
||||
|
||||
**Not undone: `osprey` and `marmalade` are already in this repository's history.**
|
||||
*Rewriting history to honour a rule adopted afterwards would cost more than it
|
||||
buys*, **and the two rounds are quotable forever, which is exactly what `v1` says
|
||||
not to want.** *Recorded rather than repaired.*
|
||||
|
||||
**Belief that could be shown wrong, and it is a real conflict:** *the annotation
|
||||
protocol this repository works under says **commit before dissolving, git is the
|
||||
only archive of the conversation.*** **An ignored cart has no archive**, so
|
||||
dissolving a notes file destroys the annotations outright. *One of the two is
|
||||
wrong and it is not ours to settle.*
|
||||
|
||||
Reference in New Issue
Block a user