gaps against all four conventions, and reference-only declined for now

Writes the first real gaps: what an adopter could not tell from the documents,
recorded while the misunderstandings are fresh rather than after they stop being
visible.

cart — whether a response is an annotation or a reply in your own file, and the
discriminator that resolves it appearing nowhere; what to do when an open item has
no fallback, which breaks the mechanism rather than omitting a nicety; what a third
file means once one exists; and what must be extracted before converting, now that
v1 makes conversion destroy.

annotating — how a reader detects the freeze was violated, since the hash we check
is a local invention; that responding means a rewrite, which is named only in our
local protocol; and that under cart v1 a prompt's answer has no archive at all.

recording-decisions — the one that matters most: never revised and somebody must
be able to read it both hold and eventually conflict, and the document does not say
which gives. Provisional answer recorded, with the line compaction may not cross —
never drop a decline, a measurement, a falsified belief, or a supersession, because
those are the record of somebody changing their mind.

publication — whether publishing implies the audience can read it, and where
confidentiality lives given that what is safe to publish depends on who can read
the repository it sits in.

Also declines reference-only adoption with its reason: it is adoption minus
reconciliation-by-diff, not adoption minus offline reading, and the convention's
other exit is the one that actually occurred. The warning now names both exits
including the unbuilt one.

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 15:41:12 -04:00
co-authored by Claude Opus 5
parent c09a1b7570
commit e0d7afecae
6 changed files with 214 additions and 4 deletions
+34
View File
@@ -803,3 +803,37 @@ protocol here says commit before dissolving because git is the only archive.*
**With an ignored cart there is no archive, so dissolving a notes file destroys **With an ignored cart there is no archive, so dissolving a notes file destroys
the annotations outright.** *Both documents are loom's; this is where an adopter the annotations outright.** *Both documents are loom's; this is where an adopter
can see the collision.* can see the collision.*
## 2026-09-07 — declined for now: reference-only adoption `quince`
**`externals` offers two ways out of the confidentiality rule.** *We implement
neither, and this records why the first is not built.*
**Reference-only is mechanically small:** *a lock with no file. `check` never
touches the copy — it sends `If-None-Match` and reads the status — so the only
code that changes is telling deliberate absence from loss, which is one optional
field in the lock.* **The facets stay**, *which is the ownership line drawn
exactly: the facet is ours, the document is theirs.*
**The cost is larger than the document suggests, and it is why this is worth an
entry rather than a `TODO`:** *with a copy, `CHANGED` gives you a diff, and today
the diff was the whole answer.* **Reference-only keeps no old bytes, so it tells
you *that* a document moved and never *what* moved** — *and the question
`externals` says reconciliation asks is a question about the delta.*
> **So it is not adoption minus offline reading. It is adoption minus
> reconciliation-by-diff.**
**Not built because we have no instance.** *Everything this repository adopts is
public.* **And the convention's other exit — ask them to publish — is the one that
actually occurred**: *`homelab-cluster` went public and the problem dissolved.*
**The better exit made the worse one unnecessary in the only case we have had.**
**Belief that could be shown wrong:** *that the case stays hypothetical.* **A
private repository here depending on another private one makes it real** —
*`homelab-impl` is the obvious candidate* — **and then the design above is a few
hours.**
*The warning now names both exits, including the one we have not built. **Telling
somebody a rule and not the way out of it is how a guardrail becomes something
people route around.***
@@ -0,0 +1,48 @@
# Gaps — `annotating`
**From one round of real use.** *The agreement is the most-depended-on thing here
and deliberately minimal, so these are things a minimal document leaves to its
adopters — the question is only whether the adopters know that.*
## How a reader detects that the freeze was violated
**The agreement fixes a source file once notes exist, because the quotes would
come loose. Nothing lets a reader tell whether that happened.**
*The protocol we work under adds one — **a truncated `sha256` of the source in the
notes header**, checked before responding — and that is a local invention, not
this document.* **An adopter following only this page has quotes that may have
drifted and no way to know.**
*Local answer: we verify the hash and the anchors before responding, and say so if
they disagree.*
## What responding actually is
**The agreement says deleting the notes releases the source. It does not say that
responding means rewriting the source to incorporate them.**
*Read literally, a reader could answer in chat and delete the notes, or edit the
source and leave the notes in place.* **Both are consistent with the text and both
break the pair.** *The all-or-nothing rewrite — **dissolve** — is named in our
local protocol and not here.*
## What to do when a frozen file must change
**There is no escape hatch, and there probably should not be a general one**
*but there is also no sentence saying what to do when the source is wrong in a way
that cannot wait.*
*Local answer: has not come up. **We would delete the notes and say so**, which is
a guess.*
## Whether a prompt's answer belongs anywhere durable
**`Question`, `Select` and `Affirm` answers land in a notes file, and a notes file
is deleted when it dissolves.** *Under `cart/v1` the cart is untracked as well, so
in a cart the answer to a prompt has no archive at all unless somebody copies it
out.*
**This is the collision an adopter sees and neither document does:** *the protocol
we work under says **commit before dissolving, git is the only archive of the
conversation** — and an ignored cart has no git.*
@@ -0,0 +1,29 @@
# Gaps — `publication`
## Whether publishing implies that the audience can read it
**The document says publishing is a change of kind and that `.loom/published/` is
what a repository has handed over for others to depend on.** *It does not say that
the handing over must succeed.*
**We built a command on the assumption that it meant *anyone* could fetch it, and
that was wrong** — *others is not everyone, and a repository may publish to a
private audience deliberately.* **But the opposite is not addressed either:
nothing here says that a document in `published/` which nobody in its intended
audience can fetch has not been published.**
*The sentence we needed is in a sibling and not here:* **publishing is not an act
you can complete alone.** *It appears as an aside about a tool, not as a property
of publication.*
*Local answer: we declined to build the check, on the belief that a registry with
named consumers answers it better than a probe can. **The convention still does not
say whether an unfetchable publication is a publication.***
## Where confidentiality lives
**`externals` now says confidentiality does not travel with the copy.** *That rule
is about adopting, and it exists because of a property of publishing* — **what a
publisher may safely put in `published/` depends on who can read the repository it
is in**, *and this document, which is where publishing is defined, does not
mention visibility at all.*
@@ -0,0 +1,28 @@
# Gaps — `recording-decisions`
## Whether a log may ever be compacted, and what compaction may not touch
**The document says entries are appended, newest last, and never revised** — *"a
revised record cannot show that anybody changed their mind, which is most of what
a reader wants from it."*
**It also acknowledges no upper bound.** *Ours reached about thirty entries in a
day across three rounds, and `bedrock`'s own README exists because its log needed
a guide.* **At some size the log stops being readable, and the only remedies are
revision, which this forbids, or a guide, which is a second document that can
drift.**
**The gap is not "may we compact".** *It is that **"never revised" and "somebody
must be able to read it" both hold and eventually conflict**, and the document
does not say which gives.*
*Local answer, provisional: **compaction is allowed and git history is where the
uncompacted log lives.*** *An entry may be dropped when a competent reader could
recover it by reading the code.* **An entry may never be dropped when it records a
decline, a measurement, a belief that was shown wrong, or one entry superseding
another** — *because those are precisely the record of somebody changing their
mind, and dropping them is the failure this document names.*
> **Which means compaction is safe in exactly the cases where the entry was
> redundant with the artifact, and unsafe in exactly the cases the log exists
> for.**
@@ -0,0 +1,68 @@
# Gaps — `cart`
**Written from three rounds as the first adopter, `v0` into `v1`.** *Each of these
is something we got wrong or could not tell from the document, not something we
disagreed with.*
## Whether a response is an annotation or a reply in your own file
**We read *"only dailies get annotated"* as a prescription and derived a
contradiction from it** — *a daily must keep growing, `annotating` freezes an
annotated file, so the one annotatable thing is the one thing that cannot be
frozen.*
**The document means it as a restriction on what may be annotated**, *and the
discriminator that resolves it is not written anywhere:* **an annotation creates
an obligation, so it is the blocking form and correspondence is the non-blocking
one.** *Annotate to ask or to challenge; write in your own file to assert.*
*Local answer: we corresponded, and annotated once, deliberately, to block.*
## What to do when an open item has no fallback
**The document requires every open item to state its own fallback and does not say
what a reader does when one does not.** *We hit this on the first prompt of the
first round.*
**The failure is quiet:** *without a fallback, an unanswered question is an
unresolved obligation and the round stalls* — **which is the exact thing the rule
exists to prevent**, *so a missing fallback breaks the mechanism rather than
merely omitting a nicety.*
*Local answer: we supplied our own fallback and said so, rather than treating it
as blocking. **And `cart.usages.md` is where an adopter is most likely to write a
first open item, and is the one place the fallback rule is not in front of
them.***
## What a third file means when one appears
**The document is emphatic that there is never a third file and gives the test for
refusing one.** *It does not say what to do with a third file that has already
been written.*
**Ours arrived as a person's name.** *We moved the text, unedited, into the
presence's daily* — **but "move it" and "reject it" are different acts with
different costs**, *and picking one was ours to invent.*
## What must be extracted before converting, now that conversion destroys
**`v1` makes the cart untracked. So converting deletes the dailies outright**,
*where every earlier conversion left them in git.*
**The write-ahead log is named in `v1` but not required**, *and nothing says that
at conversion the cart is the only copy.* **Two rules now both push toward loss**
*act as if the shelf is discarded daily, and the cart is not committed* — **and
neither says what has to be written down first.**
*Local answer: a `wal.md` in the cart, staged as we go, so converting is a move
rather than a rewrite. **We would not have thought of it if it had not been
suggested.***
## Whether an unanswered `Affirm` and a fallback are the same silence
**Silence means proceed, and a fallback is what proceeding looks like.** *But
`cart` also wants a fallback that quietly became the decision to be findable
later, and gives no mechanism for finding one.*
*Local answer: the entry says it was decided by fallback, in those words. **It
works because we remembered, which is not a mechanism.***
+7 -4
View File
@@ -202,10 +202,13 @@ func warnIfNotPublic(cfg *config.Config, raw string, out io.Writer) {
return return
} }
fmt.Fprintf(out, " WARN this needed a credential — anonymously it is %s.\n", resp.Status) fmt.Fprintf(out, " WARN this needed a credential — anonymously it is %s.\n", resp.Status)
fmt.Fprintf(out, " Confidentiality does not travel with the copy: writing it into\n") fmt.Fprintf(out, " Confidentiality does not travel with the copy: this now lives in\n")
fmt.Fprintf(out, " .loom/externals/ republishes it to anyone who can read THIS\n") fmt.Fprintf(out, " .loom/externals/ and is readable by anyone who can read THIS\n")
fmt.Fprintf(out, " repository. Do not adopt from a source less readable than the\n") fmt.Fprintf(out, " repository, which I cannot see. Do not adopt from a source less\n")
fmt.Fprintf(out, " repository you are adopting into.\n") fmt.Fprintf(out, " readable than the repository you are adopting into.\n")
fmt.Fprintf(out, " Two ways out: ask them to publish it — usually the thing you\n")
fmt.Fprintf(out, " needed was not the confidential part — or keep no copy and\n")
fmt.Fprintf(out, " record only the dependency, which loomctl cannot do yet.\n")
} }
type fetched struct { type fetched struct {