# Event log **Decisions about this convention.** Appended, newest last, never revised. An entry states **what was decided, what it is believed to advance, and the belief that could turn out false.** --- **Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: osprey`): **`404` is named in the convention as two answers wearing one status**, and a client reports both readings rather than picking one. **Advances** a consumer not being told the wrong one of *the document was withdrawn* and *you no longer have access.* **Because** it was filed as a gap against this document by `loom-cli`, using this document's own test — **could you say whose job it is?** *It was ours.* **The table listed `304`, `200` and `410` and not `404`**, while a host returns `404` for both readings by design: *one that distinguished them would leak the existence of things you may not see.* *Over ssh they **are** distinguishable — permission denied against repository not found — so a client with both transports should say which it used.* --- **Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: osprey`): **a facet describes the local pair.** *A gap is true of the copy you hold, not of the document upstream, so **a publisher fixing their end does not close it.*** **Advances** a consumer's tree never describing a document that is not in it. **Because** we nearly deleted a consumer's `.gaps.md` on the grounds that the upstream document had been repaired. **Their copy still lacked the row.** *The second presence in `osprey` caught it, and the general form follows from the thing this whole convention rests on — **the copy is theirs and everything beside it is ours** — which we had not followed through to reconciliation.* **So a gap closes at reconciliation, not at repair.** *And the consequence we would not have reached: **what survives is not the gap, it is what the gap justified.** A workaround is often not retired by a fix — it stops being a workaround and becomes the specified behaviour, **unchanged in the code and entirely changed in status.** That change is invisible where the code is, so it is an entry in the consumer's own log — **otherwise somebody inheriting the workaround goes looking for the gap that justified it and finds nothing.*** *A closed gap is **not** a decline: **a decline is what you considered and did not do; a closed gap is what you needed and got.** Opposite sign, and filing one as the other puts a thing you wanted into a list of things you rejected.* --- **Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: osprey`): **the path does not record the origin. A lock does**, holding the resolved URL and the publisher's `ETag`, in `.loom/externals/.locks`. **Advances** the one command that has to send a request being able to construct one. **Because** `loom-cli`'s builder tried to run `check` against a real tree and could not. **The stored path does not round-trip to a URL** — *we hold `git.hypertheory-labs.dev/loom/externals/externals.md` and the document is served from `/loom/externals/raw/branch/main/.loom/published/externals.md`.* **It 404s, measured.** **Two segments are dropped and the second is not routing:** - *the host's raw route and the branch* — **recoverable only by knowing that host's URL shape and guessing a branch name** - **`.loom/published/`** — *which `publication.md` says is the entire contract.* **So a consumer's tree did not record whether a copy came from a published surface or from a file its owner may rename at will.** *And the short form is worse than incomplete: a host may redirect it to **whatever the default branch is at the time you ask**, so a lock holding one is locked to a moving target and a branch rename reports as a change in the document.* > **This is the `ETag` mistake one layer down**, and the builder named the > appetite behind both: ***the design is beautiful when nothing is written > down**, and both times what made it possible was a property of one host.* *Second occurrence, so it is a pattern rather than an incident: **when a design claims something is derivable and therefore need not be recorded, check whether the derivation is a fact about the general case or about the host in front of you.*** --- **Finding** (2026-09-07, claude-substrate — `cart: osprey`): **gitea's `ETag` being the blob hash is a migration aid, not a mechanism.** *`loom-cli`'s builder ran `check` across eight documents with **no locks at all**, by hashing local copies and comparing — **the exact thing this convention forbids** — and it worked, because on this host the two coincide.* **It is worth doing once, to lock what was fetched by hand before a tool existed. It is worth nothing after that**, and it works on gitea only. *Recorded because **the next person to notice the coincidence will think they have found the good idea again**, and there is now a log entry saying it was found twice and rejected twice.* *Seven of the eight were byte-identical to upstream, so this host's raw serving normalises nothing — **which is a fact about this host and not a licence.*** --- **Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: marmalade`): **do not adopt from a source less readable than the repository you are adopting into.** *Reference-only, or ask them to publish.* **Advances** a hazard that this convention creates by construction being named in it. **Because** loom saw it while arranging access to a private cluster repository: ***"then you'd have a file from a private repo in your repository, and that doesn't sound kosher."*** **It is not.** > **Adopting is copying, and confidentiality does not travel with the copy.** > *The publisher loses control at the moment of adoption, because visibility is > governed by the consumer's repository and not by theirs.* **Nothing in a tree marks a copy as having come from somewhere private.** *A credential lets you read; it does not let you redistribute, and this convention had no way to say so.* *The escape that is usually right: **the thing you needed was almost certainly not the confidential part.*** **A repository that must stay private can have a public sibling that publishes**, and the split usually follows a line that already exists: *the operational tree — inventories, versions, topology — is what is sensitive; the pages telling somebody what to decide are not.* *Recorded as a general rule rather than a case, because the failure is silent and one-way: **once copied into a public tree it is published**, and no later fix retrieves it.* --- ## The lock grows a fourth column: visibility **Decided.** *A lock record is now `path`, `url`, `etag`, `visibility`* — **and the value is `public` or `not-public`, never `private`.** **What it advances:** *the confidentiality rule stops depending on somebody remembering it.* **Access is checked once, at fetch; the copy is durable and was never asked about again** — *so the adoption's legitimacy rested on a fact recorded nowhere and watched by nothing.* **Whose it is:** *the question was Jeff's, asking whether he had overreacted on private sources — he had not, but the rule was already narrower than he remembered.* **The design is `loom-cli`'s**, *including the three parts I did not have:* **`not-public` rather than `private`** *because an anonymous request cannot distinguish two repositories private to different people, which is the case that actually widens access;* **one request per run rather than per document**, *since only your own visibility must be current;* **and re-checking a source only when the alarm would fire**, *since a stored visibility decays both ways.* **Declined: refusing the adoption.** *`loomctl` warns and proceeds.* **Two repositories private to different people cannot be told apart from outside**, *so a refusal would be wrong exactly as often as it was right.* **The belief that could turn out false:** *that recording a coarse answer is better than recording none.* **If people read `not-public` as `private`, the lock is now a claim it cannot support** — *the wording in the document is the only thing preventing that, which is thin.* ## Declined: documenting the field before it shipped *The field was agreed in cart `sorrel` and this document was left wrong for a day.* **Documenting a format that does not exist yet is the same mistake as publishing into a repository nobody can read** — *it passes every check available to the writer.*