a changed external is a polad, and add no longer overwrites anything

Restores something the specimen said and the round that discarded the specimen
lost with it: a changed external becomes a polad in the cart, and somebody
decides. check now stages what moved into .loom/cart/current/polad/ with the ETag
that was served alongside the bytes, and prints the .usages.md beside it, because
reconciliation runs the other way — the facets usually survive and what moves is
the code a usage named. It says so when there is no usages file, which is its own
finding.

With no cart open, check reports and stages nothing. The tool does not open a
round: a cart is a bounded exchange between two presences and starting one is
somebody's act, not a side effect of asking about freshness.

add now adopts what is not here and refuses what is already adopted, superseding
the entry that had it announce an overwrite — it no longer overwrites at all. The
one exception is the only way out of a dead end: a document present but unlocked
was fetched by hand, nothing records its origin, and the path does not round-trip,
so check cannot ask about it and a refusal would strand it forever. add accepts it
and the bytes decide — identical locks it without rewriting anything, which makes
the lock's assertion verified rather than assumed, and different stages a polad and
leaves the local copy alone because it is the only evidence anything moved.

apply exists because the lock is the half a person forgets: moving a polad by hand
leaves a lock describing the copy you just replaced. Recorded with its limit —
for an external, discard does not mean the change goes away, so discarding is
really knowingly stale and nothing yet records that choice.

Measured end to end on this repository: eight hand-fetched documents, all eight
locked, nothing rewritten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018UTxuSizozEA8yDitPuris
This commit is contained in:
2026-09-07 14:57:57 -04:00
co-authored by Claude Opus 5
parent 57ed133021
commit 021bf63a11
5 changed files with 383 additions and 94 deletions
+20 -4
View File
@@ -19,10 +19,19 @@ const usage = `loomctl — fetch what you depend on, and find out when it change
loomctl external list <repo-url> what a repository publishes
loomctl external add <url> [--path p] adopt one document and lock it
loomctl external check ask every publisher whether theirs moved
loomctl external apply [path...] move a staged polad into place, lock and all
check reports and does not fix. A changed document is a candidate, not a
replacement, and somebody decides. It exits 0 whether or not anything moved:
the report is the answer.
check reports and does not fix. A document that moved is staged as a polad in
.loom/cart/current/polad/ — a candidate shaped exactly like what it would
become — and somebody decides. Its exits are apply or discard; nothing there
drifts into being kept. With no cart open, check says what moved and stages
nothing, because opening a round is somebody's act and not a side effect.
add adopts what is not here yet, and refuses what is already adopted. Given a
document that is present but unlocked — fetched by hand before this existed —
it supplies the missing origin: identical bytes lock it, differing bytes are
staged, and the local copy is never overwritten, because a copy that differs is
the only evidence that anything moved while nothing was watching.
Adopted documents live in .loom/externals/<host>/<owner>/<repo>/<name>.md, and
their origins in .loom/externals/.locks. The path is for a person to read; the
@@ -57,7 +66,7 @@ func run(args []string) error {
func runExternal(args []string) error {
if len(args) == 0 {
return fmt.Errorf("external needs a subcommand: list, add, check")
return fmt.Errorf("external needs a subcommand: list, add, check, apply")
}
switch args[0] {
case "list":
@@ -88,6 +97,13 @@ func runExternal(args []string) error {
}
return external.Check(root, os.Stdout)
case "apply":
root, err := root()
if err != nil {
return err
}
return external.Apply(root, args[1:], os.Stdout)
default:
return fmt.Errorf("unknown external subcommand %q", args[0])
}