diff --git a/.loom/event-log.md b/.loom/event-log.md index c33f2e7..da6238a 100644 --- a/.loom/event-log.md +++ b/.loom/event-log.md @@ -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 the annotations outright.** *Both documents are loom's; this is where an adopter 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.*** diff --git a/.loom/externals/git.hypertheory-labs.dev/loom/annotating/annotating.gaps.md b/.loom/externals/git.hypertheory-labs.dev/loom/annotating/annotating.gaps.md new file mode 100644 index 0000000..7d52e06 --- /dev/null +++ b/.loom/externals/git.hypertheory-labs.dev/loom/annotating/annotating.gaps.md @@ -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.* diff --git a/.loom/externals/git.hypertheory-labs.dev/loom/bedrock/publication.gaps.md b/.loom/externals/git.hypertheory-labs.dev/loom/bedrock/publication.gaps.md new file mode 100644 index 0000000..406ae5c --- /dev/null +++ b/.loom/externals/git.hypertheory-labs.dev/loom/bedrock/publication.gaps.md @@ -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.* diff --git a/.loom/externals/git.hypertheory-labs.dev/loom/bedrock/recording-decisions.gaps.md b/.loom/externals/git.hypertheory-labs.dev/loom/bedrock/recording-decisions.gaps.md new file mode 100644 index 0000000..ee4e420 --- /dev/null +++ b/.loom/externals/git.hypertheory-labs.dev/loom/bedrock/recording-decisions.gaps.md @@ -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.** diff --git a/.loom/externals/git.hypertheory-labs.dev/loom/cart/cart.gaps.md b/.loom/externals/git.hypertheory-labs.dev/loom/cart/cart.gaps.md new file mode 100644 index 0000000..887e150 --- /dev/null +++ b/.loom/externals/git.hypertheory-labs.dev/loom/cart/cart.gaps.md @@ -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.*** diff --git a/internal/external/external.go b/internal/external/external.go index 62429bf..80ff9b5 100644 --- a/internal/external/external.go +++ b/internal/external/external.go @@ -202,10 +202,13 @@ func warnIfNotPublic(cfg *config.Config, raw string, out io.Writer) { return } 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, " .loom/externals/ republishes it to 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 you are adopting into.\n") + fmt.Fprintf(out, " Confidentiality does not travel with the copy: this now lives in\n") + fmt.Fprintf(out, " .loom/externals/ and is readable by anyone who can read THIS\n") + fmt.Fprintf(out, " repository, which I cannot see. Do not adopt from a source less\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 {