quince: reconcile through the tool, write the facets, and adopt gitea.md

The whole loop ran on a real change with a cart open for the first time: check
staged externals.md as a polad, apply moved it and its lock, and the new
confidentiality rule arrived through the tool rather than through somebody saying
so.

Answers loom's completeness case. A 200 says the document moved and nothing about
whether the casting still covers it — but the mechanism exists and we had not
built the thing it needs. check prints the document's .usages.md when it stages a
polad, and for an agreement that is the file the roles are cast in. It failed here
because externals.md had no facet at all. So both are written: externals.usages.md
naming which Go file implements which rule, and a v1 section on cart.usages.md
recording the casting, that we got it wrong before it was written down, and that
osprey and marmalade stay in history unrewritten.

Implements the adopted confidentiality rule as far as it can be implemented. add
warns when a fetch needed a credential, and says plainly that it cannot see who may
read the repository the copy lands in. Reference-only adoption is recorded as not
yet implemented rather than as a gap, because the gap is ours.

Adopts gitea.md, now that homelab-cluster is public — the document that cost three
tool calls and a guess this morning, with a facet recording that its :2222 fact is
a fact for people and not for the tool, whose every transport is HTTPS on 443.

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:26:27 -04:00
co-authored by Claude Opus 5
parent c0ae0e892f
commit 6a5966ba99
8 changed files with 237 additions and 1 deletions
@@ -0,0 +1,69 @@
# The git host
**`git.hypertheory-labs.dev`**, on the public internet, with a real certificate.
**This is where the `loom/*` repositories live.**
---
## SSH is on 2222, and it is not optional to know
**Git-over-SSH does not pass through Traefik** — it is raw TCP, on a
`LoadBalancer` that binds a host port on every node. **Port 22 is held by each
node's own `sshd`**, so the service is on **2222**.
```
ssh://git@git.hypertheory-labs.dev:2222/<org>/<repo>.git
```
**A clone URL without the port will not work**, and the failure looks like an
authentication problem rather than a wrong port.
```sh
ssh -T -p 2222 git@git.hypertheory-labs.dev # "Hi there, <name>!" once a key is registered
```
*`Permission denied (publickey)` from a node IP is the **success** case for an
unregistered key — the server answered and offered its host key.*
## Registration is closed
**One account.** *The anonymous landing page serves no sign-up link.* **If you
need access, somebody creates it for you.**
## Two things that will surprise you
**Sessions do not survive a restart.** *There is no Redis or valkey here — cache
and session are in memory, deliberately.* **The queue is on disk and does
survive.** *With one user this is nearly free; it stops being free if this ever
grows real users.*
**If the control-plane node is down, this is down.** *The repository volume is
pinned to it and cannot move.* **Postgres is unaffected** — it replicates — *but
the git objects live on a volume that cannot be rescheduled.* See
[storage](storage.md).
## Never pin the chart below what is deployed
**Gitea does not migrate its schema backward.** *An older chart fails in the
`configure-gitea` init container with "database is for a newer Gitea", the
rollout hangs, and the old pod keeps serving.*
**Check `helm history` before setting a version.** *This has already happened
once.*
## The container registry
**Gitea has one. Access to it is not worked out**, and that is an open problem
rather than an omission — see [`gaps/`](../gaps/publishing-container-images.md).
---
## Checking this is still true
**Verified 2026-09-03**, after a rebuild from scratch.
```sh
kubectl get svc gitea-ssh -n gitea # EXTERNAL-IP = node IPs, 2222/TCP
kubectl get ingress -n gitea # CLASS=traefik, git.hypertheory-labs.dev
curl -sS -o /dev/null -w "%{http_code}\n" https://git.hypertheory-labs.dev/
```
@@ -0,0 +1,25 @@
# Usages — `gitea.md`
**Adopted 2026-09-07, and it is the reason this tool exists.**
*This page was published, accurate, and unreachable when it would have helped.
The `:2222` fact cost three tool calls and a guess, and the page says:* **"A clone
URL without the port will not work, and the failure looks like an authentication
problem rather than a wrong port."** *It named the failure before it happened.*
## What we use
**SSH is on `2222`, and a clone URL without the port fails as an auth error.**
*Used by anybody working in this repository by hand.* **Not used by `loomctl`**
*every transport it has is HTTPS on 443* — **which is worth saying, because it is
the difference between a fact for people and a fact for the tool.**
**Registration is closed; one account.** *Which is why
`internal/external/polad.go` and `internal/external/external.go` are built around
a single reader identity per host, and why nothing here tries to check a
publication as somebody else.*
## What we expected and did not find
**Nothing.** *The gap this document would have filled was ours, not its: it was
private, and now it is not.*
@@ -33,3 +33,34 @@ revert to.*
*Which is the argument for stating a belief rather than a preference: **ours was
shown wrong in a way we could see**, and a preference could not have been.*
---
## Cast against `v1`: the cart is not committed
**`v1` adds no role, so nothing above changed.** *It adds something an adopter
gets wrong by default, and this file said nothing about it until now.*
**`.loom/cart/` is in `.gitignore`.** *Cast in this repository on 2026-09-07,
after `v1` landed mid-round.*
> **We got this wrong before it was written down.** *Two rounds — `osprey` and
> `marmalade` — were committed, and they are still in this repository's history.*
> **Not rewritten:** *honouring a rule adopted afterwards by rewriting history
> would cost more than it buys*, **and the cost is exactly the one `v1` names** —
> *the negotiation stays quotable forever.*
**What depends on this casting:** *`.gitignore`, and
`internal/external/polad.go`* — **which stages a candidate into
`.loom/cart/current/polad/` and therefore writes only into the untracked tree.**
*If the cart were ever committed again, `check` would start proposing changes
inside version control, which is the opposite of what a polad is for.*
## An open conflict this repository cannot settle
**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.**
*Both documents are loom's. **Recorded here because the conflict is visible from
inside an adopter and not from inside either document.***
@@ -24,6 +24,28 @@ in the document.*
you fetch may refer to others; follow one when you hit something you do not know.
**Pre-resolving that is how you get a `node_modules`.***
### Confidentiality does not travel with the copy
**Adopting is copying.** *So a document from a repository somebody may not read
ends up in a repository they may* — **and the publisher loses control of it at the
moment of adoption**, because the copy's visibility is governed by your repository
and not by theirs.
> **Do not adopt from a source less readable than the repository you are adopting
> into.** *If you may read it and your readers may not, copying it publishes it.*
**Two ways out, and the second is better when it is available.**
**Reference-only***record the lock and fetch on demand, keep no copy.* **You
give up reading it offline**, which is most of what a copy is for, *and you keep
the dependency recorded and checkable.*
**Ask them to publish** — *the thing you needed was almost certainly not the
confidential part.* **A repository that must stay private can still have a public
sibling that publishes**, and the split is usually along a line that already
exists: **the operational tree is what is sensitive; the pages telling somebody
what to decide are not.**
## Two facets beside it
- **`.usages.md`** — *what we use, and **which of our artifacts depend on it***
@@ -0,0 +1,52 @@
# Usages — `externals`
**This repository implements this document.** *That is an unusual usage: most
adopters use a convention, and `loomctl` is the convention's mechanism, so nearly
every rule here names a line of code.*
## What we use, and what implements it
**Freshness is a conditional request, locked on the publisher's `ETag`,
verbatim** — *`internal/lock/lock.go`* (**the record: path, resolved URL, `ETag`**)
*and* `internal/external/external.go` (*`fetch`, which sends `If-None-Match`*).
**We compute no hash anywhere**, *which this document requires and which was
measured to matter: on gitea the `ETag` is the git blob hash and on GitHub it is
not.*
**The status table — `304`, `200`, `410`, `404`** — *`checkLocked` in
`internal/external/external.go`.* **`404` is reported unresolved, naming both
readings**, *which is this document's rule and was a gap we filed against it
before it was.*
**"The new copy is a candidate, not a replacement"** — *`internal/external/polad.go`.*
**A changed document is staged in the cart as a polad and nothing is overwritten**;
*applying is a separate act.*
**"Reconciliation runs the other way"** — *`usagesNote` and `usagesFor` in
`internal/external/polad.go`*, **which print this kind of file beside a staged
change**, *because it names the code to go and fix.* **It says so when there is
none.*
**The published surface is what `list` reads***`internal/external/list.go`,*
`.loom/published` **only.** *What is not exported is not hidden; it is simply not
what you depend on.*
**The path is for a person***`localPath` in `internal/external/external.go`.*
*This document retracted "the path says where it came from"; **we record the
resolved origin in the lock instead**, and the path is `<host>/<owner>/<repo>/<name>.md`
with `--path` for when that guess is wrong.*
## What we expected to find and did not
**Nothing outstanding.** *The `404` gap we filed here was closed on 2026-09-07 and
the facet was deleted at reconciliation.*
## Not yet implemented
**"Confidentiality does not travel with the copy"**, *added 2026-09-07.* **Nothing
in `loomctl` detects it.** *The tool knows the half it can see — whether a fetch
needed a credential — and does not yet say so.* **Reference-only adoption, which
this document offers as the first way out, does not exist either.**
*Recorded here rather than as a gap, because the gap is ours: **the document says
what to do and the tool does not do it yet.***