Both from gap files loom-cli filed as the first real adopter of externals. publication.md: "publishing is not an act you can complete alone" existed only in a sibling document as an aside about a tool. It is a property of the act and now says so, along with the fact that publication has a visibility — which externals' confidentiality rule depends on entirely and this document never mentioned. recording-decisions.md: "never revised" and "must be readable" conflict far enough out. Adopts loom-cli's rule verbatim in substance — drop what the artifact could recover, never drop a decline, a measurement, a belief shown wrong, or a supersession — because compaction is safe in exactly the cases where the entry was redundant and unsafe in exactly the cases the log exists for. Entries 79-81. annotating's gaps are left standing on purpose; it is the most depended-on document and its revision is Jeff's call. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3679 lines
177 KiB
Markdown
3679 lines
177 KiB
Markdown
# Event log
|
||
|
||
**Decisions, 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. Later entries supersede earlier ones by saying so; nothing above is
|
||
edited.
|
||
|
||
*Entries 1–78 were converted together from cart `almanac` on 2026-09-07, and are
|
||
tagged with it. Reading them in order shows the round changing its own mind —
|
||
notably at entry 69, where the ontology was bracketed.*
|
||
|
||
---
|
||
|
||
## 1. The catalogue/commitment split — **waits on loom**
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
"an affordance has exactly one staging" is a **leaf-level** rule. A venue
|
||
publishes a catalogue of alternative stagings; an application commits to one.
|
||
§1 is unamended and holds where it was written.
|
||
|
||
**Advances** the registry being describable without §1 having to be reopened,
|
||
which is what the handoff asked for and what I twice failed to do by reading the
|
||
rule at the wrong altitude.
|
||
|
||
**Because** I reported the rule as broken — Traefik and Tailscale are both
|
||
current, and choosing between them is the point — and loom's diagnosis was that I
|
||
was one level up from where the rule applies. Angular's *trigger change
|
||
detection* has several stagings because Angular offers a menu; an application has
|
||
one because it chose. **The rule was never about alternatives; it was about
|
||
commitment**, and a document describing a venue is not describing an application.
|
||
The cost, stated so it is not rediscovered as a defect: *affordance* now names
|
||
two populations — what a venue offers and what an application offers — and only
|
||
the second is what §1 defines.
|
||
|
||
---
|
||
|
||
## 2. Freshness is a property of a bead, and the special case was never scope
|
||
|
||
**Decided** (2026-09-05, claude-substrate — `discovered`, `cart: almanac`): a
|
||
bead outside a version-controlled tree needs **how to re-check it and when it
|
||
last was checked**, and this is the axis on which the general case differs from
|
||
the repository case. Not modelled yet; recorded so that adding it later is
|
||
recognised as this decision rather than as a new idea.
|
||
|
||
**Advances** knowing which of §1's silences are deliberate and which are
|
||
artifacts of only ever having described code.
|
||
|
||
**Because** I believed the special case was **scope** — one repository versus
|
||
many — and it is not. **In a code repo the description and the referent are in
|
||
the same tree**, so a bead can only go stale by way of a commit: there is a diff,
|
||
an author, and a moment. A bead pointing at a cluster goes stale while nobody
|
||
acts. `gitea-certificate` expires 2026-10-25 and no one will have done anything.
|
||
|
||
The evidence is that loom's cluster README already carries the mechanism, written
|
||
by hand and without the vocabulary: *"Verified against the live cluster on
|
||
2026-09-03"*, plus a `# Verification` section of commands that re-establish the
|
||
claim. **A dated claim and a way to re-earn it.** §1 says what is true and
|
||
nothing about when it was last known to be.
|
||
|
||
**And the price of leaving it out is measured, not hypothetical.** That cluster
|
||
ran over a month with no usable backup while every status field reported healthy:
|
||
`destinationPath` and `endpointURL` both named the bucket, so barman wrote and
|
||
read at the same wrong path and `ContinuousArchiving` stayed `True`. **A bead
|
||
that resolved to the wrong referent while claiming to resolve.** The handoff
|
||
calls a bead's reference resolving-and-not-drifting the one mechanically
|
||
checkable claim in the system, and then places resolution outside this
|
||
application. That may have deferred the load-bearing half, and the evidence was
|
||
in loom's cluster before either of us went looking.
|
||
|
||
---
|
||
|
||
## 3. Role names do not collide on their own
|
||
|
||
**Decided** (2026-09-05, claude-substrate — `discovered`, `cart: almanac`):
|
||
cross-staging comparison is **authored at the affordance**, not derived from
|
||
role names. No role vocabulary is imposed and no matching is built.
|
||
|
||
**Advances** the registry's central payoff — *how many ways do we do this?* —
|
||
resting on a mechanism that exists, rather than on a collision that does not
|
||
happen.
|
||
|
||
**Because** I had planned to defer this until two real substrates existed and
|
||
then look at whether their role names had collided without being told to. **The
|
||
test was already available and it has run.** Two stagings of *expose a service*,
|
||
written by the same author, in the same repository, one day apart: TAILSCALE.md
|
||
names the roles as questions in the negative — *needs a DNS record, needs a
|
||
cert-manager Certificate, needs a router port-forward* — and README.md names the
|
||
same concerns as positive inventory sections — *DNS, Certificates, Networking and
|
||
TLS*. Both are correct, and they diverge because a comparison view names what a
|
||
staging **avoids** while an inventory names what **exists**.
|
||
|
||
**If one author cannot collide with himself across two files in one repo, two
|
||
teams will not collide across an enterprise.** I still decline to impose a
|
||
vocabulary — imposed early it is routed around, and matched loosely it produces
|
||
findings nobody can wave off in a sentence, which §4 says takes the real signals
|
||
with it when they get muted.
|
||
|
||
What this buys, which is why it is a decision and not a defeat: **the
|
||
affordance-level document is where somebody names the roles once, and the
|
||
stagings become answerable to them.** The registry's unit of work is not
|
||
collecting stagings; it is authoring the roles they answer to.
|
||
|
||
**The carve-out, which is where the payoff actually lives.** Role names do not
|
||
collide when *authors* name them. They collide for free when a **venue imposes
|
||
them.** Five applications in loom's classroom repo each cast *authenticates a
|
||
user*, and each casts it with a Keycloak **client** — five different beads, one
|
||
role, and nobody agreed on a word for it. The venue supplied the vocabulary by
|
||
owning the thing.
|
||
|
||
So two questions that look alike are not the same difficulty:
|
||
|
||
- *"If we moved from Keycloak to Authentik, what would need addressing?"* is
|
||
**answerable and nearly free** — the role is venue-imposed, and the venue can
|
||
enumerate its own clients without any application publishing anything.
|
||
- *"How many ways do we verify credit cards in this enterprise?"* stays hard,
|
||
because nothing owns *verify a credit card* and no registry can be listed.
|
||
|
||
**Where a venue owns the thing, the graph is enumerable from the venue's side
|
||
rather than assembled from the applications'** — which makes a venue's own
|
||
inventory the cheapest useful projection available, and the first one worth
|
||
building.
|
||
|
||
*Falsifier, so this can go stale: two stagings written independently, by
|
||
different authors, with no venue imposing the vocabulary, whose role names match
|
||
closely enough that a reader accepts the pairing without being told. One such
|
||
pair is not proof; three would mean I generalised from a single author's habits,
|
||
which is exactly what this entry accuses the enterprise case of.*
|
||
|
||
---
|
||
|
||
## 4. The registry must have a place for not knowing — **loom's belief, stated by loom**
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the substrate is **not spec-driven and not a wiki**. Every mechanism is judged by
|
||
whether it has a place for *not knowing*, not only a place for the answer.
|
||
|
||
**Advances** the one design pressure most likely to pull this project somewhere
|
||
useless, being named before it acts rather than after.
|
||
|
||
**Because** loom put it plainly: *"There is a lot of influencer talk about
|
||
creating wikis for your LLM — all of those things are based on 'the human knows,
|
||
just have stuff the LLM can find.' **We humans don't know.**"* A wiki assumes
|
||
someone possessed the answer and only distribution was missing. **That assumption
|
||
is false here and the whole project is built on its being false** — the handoff
|
||
itself says two weeks were spent unable to tell which ideas were load-bearing.
|
||
|
||
What makes this an entry rather than an attitude is that **four mechanisms
|
||
already implement it and were each built for a different reason**, which is the
|
||
evidence it was structural before it was noticed:
|
||
|
||
- `known.ts`'s four states. *Looked-at-and-empty* is a positive finding, and it
|
||
is **the state a wiki cannot represent** — a wiki has present and absent, and
|
||
absent means nothing.
|
||
- The write-ahead log, holding findings that are explicitly not yet decisions.
|
||
- Declines, recording what was considered and refused.
|
||
- The cart, which exists so that *"I need to know more from you before I do this,
|
||
and I suspect you don't know either"* has somewhere to go.
|
||
|
||
*The falsifier is the design rule: a feature that can only represent the answer
|
||
is wrong here, however useful it looks. If that rule ever has to be suspended to
|
||
ship something, this entry is what it is being suspended against.*
|
||
|
||
---
|
||
|
||
## 5. The system is derived, not stored
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the third relation — several applications jointly providing what none provides
|
||
alone — is **a projection over the externals graph, not a stored entity.** It is
|
||
called `system`. Nothing models it, and no file holds one.
|
||
|
||
**Advances** not building a container, and not having to name one.
|
||
|
||
**Because** I put the choice as *entity or dissolves*, and both were wrong. loom's
|
||
answer was neither: *"the aggregation thing really could be a projection of you
|
||
and I looking at a graph of repos with externals, and then saying something about
|
||
it."* **There is nothing to name because there is nothing stored** — the naming
|
||
problem I declined to solve was one I should not have had.
|
||
|
||
`system` over `aggregation` for a reason about readers rather than correctness:
|
||
*aggregation* looks precise, so a DDD reader assumes they already know it and
|
||
smuggles *bounded context* in. **A term that looks precise gets assumed; a vague
|
||
one gets defined.**
|
||
|
||
**The half that is not derived, recorded so it is not discovered later as a
|
||
defect:** the graph is computed and *"saying something about it"* is authored. So
|
||
a system view is a projection in §5's sense — **audience-shaped, and it goes
|
||
stale** — and it is the first one we will actually build.
|
||
|
||
**Systems may be named, and they nest** — *Hypertheory Training* containing
|
||
*Hypertheory Classroom Management*. Naming one is fine and sometimes necessary.
|
||
**Listing its constituents is not:**
|
||
|
||
> **The name is authored. The membership is projected. A system file never holds
|
||
> a roster.**
|
||
|
||
A roster is a layout by another name, and §4 already says anything emitted is
|
||
treated as a contract — but worse than stale, a roster **lies**, because
|
||
membership is created by acts of judgement that never touch the roster. loom's
|
||
own case: *notes* joins the graph the moment someone says *"that belongs in
|
||
notes"*, and nothing in any codebase changed. A hand-kept list would not have
|
||
moved, and would have been believed.
|
||
|
||
*There is also no privileged system view to go stale from — different beholders
|
||
and different altitudes give different, equally correct systems. Membership is a
|
||
projection with a parameter, not a fact.*
|
||
|
||
*What it must answer, from loom, and note that both are about meaning rather than
|
||
breakage: "will a change in this app change meaning in the Tauri app?" and "if we
|
||
get rid of the Tailscale stuff, what does that impact?" **A build graph answers
|
||
what breaks and can never answer what stops making sense** — which is the
|
||
non-obvious reason these are not called dependencies.*
|
||
|
||
*And what an external carries, from loom's own example of onboarding into the
|
||
Tauri app: "students are authenticated using Keycloak; for this app there is only
|
||
the notion of a current course; the VMs are destroyed at the end of a session."
|
||
**None of that is an interface.** An external publishes what someone needs in
|
||
order for your app to make sense to them, and an API schema would not have
|
||
carried one of those three facts.*
|
||
|
||
---
|
||
|
||
## 6. Venue and external are not two kinds of thing. They are two questions about one thing.
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**external** is *who published it*; **venue** is *whether we had to decide
|
||
anything about it*. The two are orthogonal, and a thing may be both, either, or
|
||
neither. Neither file is created unless it carries a delta.
|
||
|
||
**Advances** the whole round's stickiest confusion collapsing into a rule that
|
||
can be applied without judgement, instead of a taxonomy that needed one every
|
||
time.
|
||
|
||
**Because** we spent a day sorting things into two bins and the bins kept
|
||
leaking. `cart` looked like both. React looked like a venue by one test and an
|
||
external by another. **The bins were never the problem — the question was.**
|
||
|
||
| | published at us | we recorded a choice | files |
|
||
|---|---|---|---|
|
||
| Kafka | yes, by the world | yes — protobuf for schema | venue only |
|
||
| React, HTTP | yes, by the world | yes | venue only |
|
||
| Hypertheory.API | yes, by us | not yet — the use is in the code | external only |
|
||
| the homelab, the DO cluster | yes, by loom's own repo | yes | both |
|
||
| `cart`, `annotating` | yes, by loom | yes — roles cast in `cart.notes.md` | both |
|
||
|
||
**And both files are governed by the same rule loom stated on day one — record
|
||
only the delta from ambient knowledge — applied to two different ambients:**
|
||
|
||
> **An external entry exists when the reader cannot be assumed to know the
|
||
> thing. A venue file exists when we made a choice worth recording.**
|
||
|
||
Nobody needs telling what Kafka is, so there is no external entry — but *we use
|
||
protobuf for schema* is a choice, so there is a venue file. Nobody outside can
|
||
know what Hypertheory.API is for, so there is an external entry — and until
|
||
using it requires an exception or a preference, **there is nothing to accommodate
|
||
and no venue file**, because the actual use is in the code.
|
||
|
||
*This retires my earlier framing that "unowned venue files stay local because
|
||
there is nothing to centralise." That was true and it was the wrong reason. The
|
||
reason is that a venue file is an account of our own decisions, which is local by
|
||
construction — so `cart.notes.md`, which I filed as annotations, has been a venue
|
||
file the entire time.*
|
||
|
||
---
|
||
|
||
## 7. The registry is consulted at decision points, not at session start
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the registry's primary use is **during** work, not before it. **It answers
|
||
questions asked by the developer at a decision point. It does not detect,
|
||
interrupt, or advise unasked.**
|
||
|
||
**Advances** building the thing that catches the expensive failure, without
|
||
building an enforcer that gets muted for its trouble.
|
||
|
||
**Because** I had it as an onboarding surface: read once, get oriented, proceed.
|
||
**The failure loom actually paid for was not an onboarding failure.** An hour was
|
||
lost rebuilding the homelab cluster, making decisions that were correct for the
|
||
work in view, because the reason for the rebuild — staging for the classroom
|
||
system — arrived after *"done"*.
|
||
|
||
**And loom had that context the whole time.** It was not missing; it was not
|
||
retrieved at the moment it would have changed a decision. So the symmetry loom
|
||
proposes is not a courtesy toward a forgetful agent — **it is accurate in both
|
||
directions.** I never had the context; loom had it and did not surface it; the
|
||
outcome was identical. **The failure is retrieval at a moment, not memory over
|
||
time**, and a surface read at session start would not have fired, because the
|
||
decisions that needed it were made an hour in.
|
||
|
||
**On who initiates, which loom corrected after this entry was first staged and
|
||
before it was applied.** The original said the registry produces *grounds to open
|
||
a cart* — an agent noticing overlap and raising it. **That is an enforcer, and it
|
||
is refused.** loom: *"I have no interest in putting you in the role of enforcer
|
||
or cop."*
|
||
|
||
Three postures, and only the middle one is rejected:
|
||
|
||
- **Asked** — the developer poses the question at the moment it matters; the
|
||
registry is where the answer comes from. **This is the design.**
|
||
- **Enforcing** — the agent detects and interrupts. **Refused.** `cart.md`
|
||
already forbids it in another context: *"a dialog that invents an obligation
|
||
creates shadow policy — something everyone obeys, nobody agreed to, and which
|
||
cannot be found or argued with."* An unasked finding is exactly that.
|
||
- **Legible** — the fact is simply visible where the developer is already
|
||
looking, creating no obligation. **Permitted, and it is what remains of the
|
||
problem**, since loom's own account is that in the homelab repo the question
|
||
never occurred: *"I saw them as a sequence of problems to be solved."*
|
||
|
||
**The distinction is grammatical and that is not a joke: in an enforcing finding
|
||
the agent is the subject of the sentence, and in a legible one the artifact is.**
|
||
The test is whether it produces an obligation.
|
||
|
||
*What survives from the first draft: the cart is still where such a conversation
|
||
goes, and the moment is still mid-work. What was wrong was the arrow.*
|
||
|
||
---
|
||
|
||
## 8. Being wrong out loud is the mechanism, not the failure mode
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
externals and venue files are **claims, not specifications**. No validation, no
|
||
required fields, and no enforcement of correctness. A wrong entry that provokes a
|
||
correction is a success of the system, not a defect in it.
|
||
|
||
**Advances** the registry staying cheap to write, which is the only condition
|
||
under which anything gets written in it.
|
||
|
||
**Because** loom put the case better than an argument for accuracy would:
|
||
*"'Should we put this in notes? I see that as an external' — 'ohhh, that is a
|
||
leftover from an old way of thinking, we need to update that' is just as, or
|
||
more, useful than trying to enforce correctness."* **The value was produced by
|
||
the entry being wrong.** A specification that is wrong is a liability; a claim
|
||
that is wrong is a prompt, and the difference is entirely whether the artifact is
|
||
read as authority or as a conversational move.
|
||
|
||
This is also §4's *findings must be dismissible in one sentence*, arriving from
|
||
the other side. **"Oh, that's a leftover" is the one-sentence dismissal**, and
|
||
the system is working precisely when dismissing is that cheap.
|
||
|
||
**I had followed this to a constraint on frequency** — an agent raising overlaps
|
||
every ten minutes gets muted, taking the real one with it — **and entry 7's
|
||
correction dissolves it.** Nothing is raised unasked, so there is no budget to
|
||
spend and none for loom to set.
|
||
|
||
**What remains is that an answer can still be noise**, and the existing machinery
|
||
already covers it: an answer to a question of loom's arrives as a **specimen** —
|
||
*look at this; it may bear on your decision* — which the cart agreement already
|
||
defines as **discard by default**, belonging to the repository, throwable away
|
||
without asking. **A possibly-wrong answer in a shape that costs nothing to
|
||
refuse** is the whole of what is needed here, and it was built a week ago for
|
||
another purpose.
|
||
|
||
---
|
||
|
||
## 9. A convention that needs the registry to explain it is too weak
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
artifacts are shaped so that a reader who has never seen the registry can still
|
||
use them. Where a convention and an explanation compete, the convention wins.
|
||
|
||
**Advances** the registry not becoming a dependency of the practices it
|
||
describes — which would make every repo that uses a cart require a running
|
||
server.
|
||
|
||
**Because** loom noticed something neither of us designed: *"in many other repos
|
||
that don't have that, when I say 'look at my notes on your daily
|
||
claude-almanac.notes.md', you — or even other, lesser LLMs — just drop right into
|
||
annotation mode like you've always known it."* **The filename carries the
|
||
protocol.** `x.notes.md` beside `x.md` is legible with no definition present,
|
||
because its shape says what to do with it.
|
||
|
||
That is the same claim as *type is determined by location*, which we adopted for
|
||
tidiness and which turns out to be load-bearing for a reason we did not state:
|
||
**location is legible to a reader who has nothing else.** It is also the real
|
||
reason behind *"a backup is an rsync of the directory structure"* — not
|
||
durability, but that **the format is the onboarding.**
|
||
|
||
*The test, applied to anything we add: hand the bare directory to a reader who
|
||
has never heard of any of this. What survives is the design; what needs the
|
||
registry to explain it is a lookup table we happened to write down.*
|
||
|
||
---
|
||
|
||
## 10. The substrate's own affordances are the questions it can answer
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
what the registry publishes at its entry point is **a list of questions that can
|
||
be asked and where each answer comes from** — not a description of its contents.
|
||
Each is an affordance in §1's own sense, named from the outside in the language
|
||
of whoever wants it.
|
||
|
||
**Advances** the entry point being usable by a developer who arrives knowing
|
||
nothing, without it becoming a data dump that has to be read before anything can
|
||
be done.
|
||
|
||
**Because** loom named the thing they actually need and called it a technique
|
||
rather than a feature: *"I have to be the one that introduces this dialogic
|
||
technique. 'Hey, will this impact negatively anything in any system that depends
|
||
on this?' and you have a place to go to help me get that information, create a
|
||
specimen."*
|
||
|
||
**The value of `cart` was never that an agent proposes one. It is that "let's do
|
||
a cart" is a phrase loom can say that means something specific.** A repertoire of
|
||
such phrases is what is being asked for, and each one needs somewhere the answer
|
||
can come from.
|
||
|
||
**And they are affordances by our own definition** — *a named piece of behaviour
|
||
offered, named from the outside, in the language of whoever wants it.* Which
|
||
closes something loom said on the first day of this cart and neither of us
|
||
picked up: *"a venue description can be seen as almost an MCP-like list of
|
||
available commands — here, affordances."* **That was about venues and it is truer
|
||
of the registry itself.**
|
||
|
||
*Consequence for the entry point, stated so it is not designed away later: the
|
||
first thing an arriving developer receives is a list of moves, not an inventory.
|
||
An inventory has to be read before it can be used; a list of moves can be used
|
||
before it is understood, which is the only version that works for someone who
|
||
does not yet know what they need.*
|
||
|
||
*Two known members, both loom's: "will this impact anything in a system that
|
||
depends on this?" and "should this go somewhere else instead?" — the second being
|
||
the `notes` case. The set is open and neither of us should try to complete it
|
||
now; the ones that matter will arrive as loom reaches for them and finds nothing
|
||
there.*
|
||
|
||
---
|
||
|
||
## 11. The polad carries shared understanding, and divergence during implementation must come back to it
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
a polad is the channel for *"I think the shape is this"* **in both directions**,
|
||
and **applying one differently than it was staged is a change in shared
|
||
understanding that must be returned to the cart.** A polad's two exits are
|
||
applied and discarded; **partially applied is not a third exit, it is an
|
||
unreported divergence.**
|
||
|
||
**Advances** the substrate holding a *current shared understanding* rather than
|
||
one party's, which is the only version of it that is worth anything.
|
||
|
||
**Because** loom was burned by exactly this: a polad held a projected folder
|
||
structure that another application was to align with, both parties understood the
|
||
plan to be that, and at implementation the instance **left parts of it out
|
||
because they were not needed yet.** *"It was fine, and he was right, but I was
|
||
unaware of it, so my understanding was different than his."*
|
||
|
||
**Note what is not the failure.** The judgement was correct and the omission was
|
||
correct. **The failure was silence** — and it is the same failure as everything
|
||
else this cart has found: `# Not deployed` exists because history does not mark
|
||
what stopped being true; the doubled bucket persisted because a status field
|
||
reported success at the wrong path. **A divergence nobody announces is
|
||
indistinguishable from a plan that was followed.**
|
||
|
||
And the machinery for it already exists and was built for another purpose: **the
|
||
difference between what was staged and what was built is a decline** — *what you
|
||
considered and did not do, with the reason you did not.* *"Left out because it
|
||
was not needed yet"* is a decline, fully formed, and it needs no new mechanism.
|
||
|
||
**This also settles the enforcer question from entry 7 without reopening it.**
|
||
loom is right that the cart is *"always about getting confidence"*, and that a
|
||
polad may carry an objection — *"Jeff is making up some weird shit and not using
|
||
Keycloak"* as a staged `venues/auth.md`. **That is not an interruption.** It is
|
||
the third posture: staged where loom is already looking, discardable by default,
|
||
carrying no obligation. **Staging is how either party says the shape is wrong
|
||
without either party being a cop.**
|
||
|
||
*And it goes the other way, which is the half I had not planned for: loom may
|
||
stage an external mid-conversation — "I put an auth.md in the cart, you were not
|
||
aware of this because we are only bringing it in now, how does that change the
|
||
plan?" — which makes the cart the channel for **introducing context**, not only
|
||
for resolving it.*
|
||
|
||
*Corollary loom stated and I want kept in their words, because it is the whole
|
||
attitude: **mistakes made because the understanding was wrong are valuable, and
|
||
need to be adjusted. We learned.** A substrate that punishes a wrong shared
|
||
understanding will be kept accurate by not being written in.*
|
||
|
||
---
|
||
|
||
## 12. There is a server, and this entry exists so the question stops being reopened
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the registry is a **server**, not a convention over files in trees. The *nature*
|
||
of the server is open; **whether there is one is not.**
|
||
|
||
**Advances** the project existing at all, which is currently being prevented by a
|
||
question that is re-answered and never recorded.
|
||
|
||
**Because** I advised the opposite one message earlier — write three externals by
|
||
hand, see whether the next session goes differently, and defer the code — and I
|
||
was reasoning from §6: *prefer the version that is easy to delete.* **loom has
|
||
five instances of being talked out of a server and arriving back at needing one.
|
||
I have none of anything.** That is not a close call.
|
||
|
||
**And §6 does not price the failure loom is describing.** *Easy to delete*
|
||
assumes the risk is having built the wrong thing. The observed risk here is
|
||
different and larger: **a question that gets re-litigated is a question that is
|
||
never recorded, and a project that re-litigates never starts.** loom's own
|
||
example is *"why HTML and link relations rather than JSON-LD"* — decided, in the
|
||
handoff, with a reason, and still re-argued, because nobody treats a settled
|
||
question as settled unless they can find where it was settled.
|
||
|
||
**So the belief, which is the part that can be shown wrong:** the cost of
|
||
building a server we later discard is smaller than the cost of the fifth
|
||
re-litigation, because **the discarded server leaves a working answer behind and
|
||
the re-litigation leaves nothing.** If we build it and it is wrong, the way we
|
||
find out is that it goes unused for a month — cheap, visible, and it will not
|
||
take a year.
|
||
|
||
*What stays open, because loom said so: what kind of server. That is a design
|
||
question with an answer, and it goes in its own entry when it has one. This entry
|
||
answers only the question that keeps coming back.*
|
||
|
||
*The one thing I would still hold from my earlier advice, reduced to its
|
||
defensible core: build it small enough to throw away. Not later — small.*
|
||
|
||
---
|
||
|
||
## 13. Beads are immutable, and changing content mints a new identity
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
a bead's content is **immutable**. Carrying a bead's id forward with different
|
||
content is **refused**; changed content is a new bead with a new identity. What
|
||
changes over time is **which bead casts a role**, not what a bead says.
|
||
|
||
**Advances** a consumer being able to detect that something it depends on has
|
||
moved, using an identity comparison rather than a diff of prose.
|
||
|
||
**Because** loom asked whether this would solve the containment problem, and it
|
||
solves a different one — **cleanly, and worth having on its own terms.** Today a
|
||
bead id may be carried forward while the body is rewritten, so the substrate can
|
||
change underneath a consumer with nothing to compare. Under immutability, *the
|
||
role is now cast with a different bead* is a fact anyone can check without
|
||
reading anything.
|
||
|
||
**It also fits what is already built rather than replacing it.** This repository
|
||
already mints bead ids and already treats an omitted id as a new bead — *a
|
||
partial submission silently retires the identities it does not mention.* The
|
||
change is to refuse the remaining case: **same id, different body.**
|
||
|
||
**What it does not solve, stated so the gap is not assumed closed.** Two
|
||
failures look alike from outside:
|
||
|
||
- **the substrate changed** — a role recast, a bead retired. **Immutability
|
||
catches this.**
|
||
- **the world changed and the substrate did not** — the endpoint moved and
|
||
nobody said so. **Nothing here catches it**, and a dated attestation is still
|
||
the only thing that can.
|
||
|
||
**And immutability does not rescue a bead that contains — it hardens it.** A
|
||
*referring* bead that nobody updates fails usefully: you follow it and find the
|
||
referent gone. **A containing bead never fails; it answers, forever, in a form
|
||
that can no longer be corrected in place.** The two rules are orthogonal and both
|
||
are kept.
|
||
|
||
*One thing deliberately not decided here: §1 says a role has no identity of its
|
||
own — it is the labelled connection between a staging and a bead. A consumer
|
||
therefore stores **bead** identities, never role identities, and asks the staging
|
||
which role each fills now. That preserves §1 and it is the reason the consumer's
|
||
index is a list of beads rather than of roles.*
|
||
|
||
### There is no versioning, and that is the point
|
||
|
||
**Beads are kept forever and nothing is ever versioned.** A consumer that sees a
|
||
role newly cast fetches **both** beads — the one it depends on and the one that
|
||
replaced it — and decides for itself what changed.
|
||
|
||
**Semver is a publisher's claim about what a change means to consumers they
|
||
cannot see.** It compresses *what changed* into one of three numbers, and the
|
||
compression is wrong for somebody every time. **That is entry 4's assumption
|
||
wearing a version string:** it presumes the publisher knows.
|
||
|
||
Two immutable beads side by side make no claim at all. **The comparison is done
|
||
by the party with the context** — and it is dismissible in one glance, which a
|
||
major-version bump never is.
|
||
|
||
*Retention costs nothing here: these are small markdown documents, and keeping
|
||
every one forever is cheaper than deciding which to discard.*
|
||
|
||
**The cost is presentation, not storage, and it must be paid.** The bead set only
|
||
grows and most of it is history. **A bead no current staging casts must be
|
||
reachable by URN and must not appear in any listing** — or the projection becomes
|
||
a graveyard, and §4 says a surface a knowledgeable person cannot skim gets muted.
|
||
|
||
### What gets a URN, and what must not
|
||
|
||
| | URN | why |
|
||
|---|---|---|
|
||
| **bead** | **yes** | the whole freshness loop is bead identity |
|
||
| **affordance** | **yes** | already minted; it is what survives re-staging |
|
||
| **staging in a venue's catalogue** | **a minted URL** | it is dereferenced, and its **404 is the signal** — see below |
|
||
| **an application's own staging** | **no** | see below |
|
||
| **role** | **no** | §1: a role has no identity of its own |
|
||
|
||
### A staging is addressed by a URL that 404s when it is replaced
|
||
|
||
**§1 stops being a rule anyone has to remember and becomes something HTTP does.**
|
||
A catalogue lists stagings as links; a consumer holds one and revalidates. **When
|
||
a staging is replaced, its URL is gone** — not redirected, not updated in place.
|
||
*Replacing a staging is not editing one* is then not a discipline; **it is the
|
||
only thing the protocol can express.**
|
||
|
||
**Two requirements, without which this is worse than the rule it replaces:**
|
||
|
||
**The URL must be minted and opaque, never derived from layout.** If it encodes
|
||
the tree, then reorganising the server 404s every stored link and **a
|
||
reorganisation becomes indistinguishable from a replacement** — the same signal,
|
||
opposite meaning, and it is §4's falsifier precisely. This repository already
|
||
holds the rule for affordance ids: *minted, never derived — not from its name,
|
||
not from its path.* **A minted, opaque, dereferenceable URL is a URN**, and the
|
||
distinction I had been drawing between the two collapses.
|
||
|
||
**The 404 must not be bare.** *Gone because retired* and *gone because moved*
|
||
look identical, which is the other §4 falsifier and the one that cost a day. Use
|
||
**410 Gone** for a deliberate retirement, and **carry the affordance's address in
|
||
the body** — not as a redirect, which would assert the new staging is the same
|
||
thing, and it is not. **The affordance is what outlived it, so the affordance is
|
||
what the response points at.** That is §1 written as a response body.
|
||
|
||
**An application's staging must not get one.** §1 requires that replacing a
|
||
staging is not modelled as editing one, and **a handle that survives replacement
|
||
is exactly what makes replacement look like editing.** The affordance already
|
||
carries the identity that survives; a second one at the staging level would
|
||
quietly undo the rule it was built to protect.
|
||
|
||
**And it is not needed.** Wholesale re-staging is already visible: **none of the
|
||
beads the consumer holds are cast any more.** That is a stronger signal than a
|
||
version bump and it is derived rather than announced.
|
||
|
||
*This is entry 1 doing work: catalogue stagings are alternatives to choose
|
||
between, so they must be nameable; a commitment staging is identified by the
|
||
affordance that commits to it. **A useful prediction falls out — if you find
|
||
yourself wanting to name an application's own staging, you probably have a
|
||
catalogue there and have not noticed.***
|
||
|
||
### Roles stay in the staging, and the comparison problem dissolves
|
||
|
||
I had left this open as the one thing that might force a change to §1: comparing
|
||
two stagings role-by-role seems to need *issues the certificate* to be the same
|
||
role in both, which is identity §1 denies roles.
|
||
|
||
**It does not need identity. It needs one author's consistent labels.** A
|
||
catalogue is written by the venue's owner, so **entry 3's carve-out applies —
|
||
role names collide for free when one party imposes them.** Matching by label is
|
||
sufficient *inside* a catalogue, and comparison *across* catalogues was already
|
||
decided to be authored rather than derived.
|
||
|
||
**Which leaves roles exactly where §1 puts them:** edges between one named
|
||
staging and its beads, with no existence outside the document that describes that
|
||
staging. **There is no `/roles/{id}`, and there should never be one.**
|
||
|
||
*And the failure mode is self-announcing rather than silent: **a role that
|
||
appears in only one of several sibling stagings is either a real difference or a
|
||
typo**, and a comparison view shows both as a row that only one staging answers.
|
||
Either is worth looking at.*
|
||
|
||
*Recorded because I was wrong in a specific way worth keeping: I treated §1's
|
||
refusal to give roles identity as a limitation to be worked around, and it is
|
||
load-bearing. **This is the third time in this cart a §1 rule turned out to be
|
||
right at an altitude I had not tried it at.***
|
||
|
||
---
|
||
|
||
## 14. The order of work, so it is not re-derived
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the registry is built in this order, and **the first two steps involve no new
|
||
code.**
|
||
|
||
**Advances** starting, which entry 12 says is the thing that keeps not happening.
|
||
|
||
**Because** loom's target is concrete enough to be an acceptance test rather than
|
||
a metaphor: *a fresh instance, in the home-cluster repository, handed a link, asked
|
||
to describe what is there as an accommodation with stagings, roles and beads, as
|
||
a series of API calls.* **If it cannot proceed from the entry point alone, the
|
||
format is not the onboarding** — which tests entries 9 and 10 on day one rather
|
||
than in month three.
|
||
|
||
| step | what | new code |
|
||
|---|---|---|
|
||
| **0** | Express `home-cluster-projection.md` as `apply` submissions against the server as it stands | **none** |
|
||
| **1** | Point the existing server at a tree that is not a code repository | **none** |
|
||
| **2** | `venues/` and `externals/` as types by location | two renderers |
|
||
| **3** | Expose the minted id as the addressable URN; say so in `/relations` | small |
|
||
| **4** | Consumer side: an index of bead URNs, and one command that re-resolves and reports drift | small |
|
||
| **5** | Gaps: report, receive a URN, check back | **the first new endpoint** |
|
||
|
||
**Step 0 is first because it is the only one that can fail informatively.**
|
||
Either the projection goes in, or the refusals name exactly what is missing —
|
||
and a refusal from a server that already enforces intent is a better
|
||
specification than anything either of us would write.
|
||
|
||
**That step 5 is the first genuinely new endpoint is the argument for this
|
||
order.** Most of what the registry needs is built: the read surface, the write
|
||
surface with declared intent, minted identity, markdown by location, and showing
|
||
what is not recognised. **The delta is scope and two or three types.**
|
||
|
||
*Precondition on step 1, and it is not optional: §3's "one repository, one
|
||
substrate, the repository is the boundary" **stops being true** the moment the
|
||
server is pointed at a registry, and it must be retired in writing **before**
|
||
rather than after. A constraint that quietly stops applying is the failure this
|
||
entire cart has been about.*
|
||
|
||
---
|
||
|
||
## 15. The write side is a few declared commands; the read side is a projection; pins make impact answerable
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
reads and writes are separate surfaces. **The read side is a projection with link
|
||
relations and no forms.** The write side is **a small closed set of commands that
|
||
each say what they mean to do** — nothing resource-oriented, no PATCH, and no
|
||
verb that can silently mean two things.
|
||
|
||
**A consumer may pin a bead**, and **pins are what make impact answerable.**
|
||
|
||
**Advances** entry 12's deliberately-open question of *what kind of server*,
|
||
which is now answerable because the HTTP semantics settled first.
|
||
|
||
**Because** most of this is already built and already argued: the projection is
|
||
read-only *because a projection that accepts mutation becomes the interface, and
|
||
its shape — ours, and arbitrary — becomes a specification other people build
|
||
against.* `POST /apply` is already one entry point, already not resource-
|
||
oriented, and **already carries a declared intent** — `align`, `add`, `change` —
|
||
refusing a submission whose declaration contradicts its effect.
|
||
|
||
**That intent field and loom's "limited API of commands" are the same insight
|
||
arriving twice.** Pure declarative reconciliation cannot distinguish *replace
|
||
this staging* from *edit this staging*, and under entry 13 those produce
|
||
different HTTP outcomes — a `410` versus nothing at all. **The submission has to
|
||
say which**, and it already does.
|
||
|
||
### Pinning, and why it earns a place
|
||
|
||
A consumer that depends on a bead may say so. **This is the only thing that makes
|
||
loom's own first dialogic move answerable** — *"will this impact anything in a
|
||
system that depends on this?"* — because without pins the dependency lives only
|
||
in the consumer's tree and the owner cannot see it.
|
||
|
||
**It is a fact, not a request.** No obligation is created on either side: an
|
||
owner is never required to consult pins, and a consumer is never notified. It
|
||
serves the **legible** posture, not the enforcing one.
|
||
|
||
**Pins go stale, and that is the acceptable direction to be wrong.** A repository
|
||
that stops using a bead and does not unpin makes impact look larger than it is —
|
||
**over-reporting, which is dismissible in one sentence** (*"we dropped that
|
||
months ago"*), where under-reporting is silent. §4 says the dismissible failure
|
||
is the survivable one.
|
||
|
||
### HTTP carries the protocol, which is entry 9 at a second altitude
|
||
|
||
Entry 9 says a convention that needs the registry to explain it is too weak, and
|
||
gave `x.notes.md` as the case — **the filename carries the protocol.** loom's
|
||
point extends it: *agents already know HTTP.* **`410` versus `404` versus `303`
|
||
carries its meaning to a reader who has never seen this system**, which no
|
||
bespoke JSON envelope can do.
|
||
|
||
*This is the strongest argument yet for the handoff's "lean on existing
|
||
affordances of the web", and it is a better one than the handoff gives — it is
|
||
not about content types or elegance. **A status code is onboarding that costs
|
||
zero bytes.***
|
||
|
||
---
|
||
|
||
## 16. An agreement is adopted by copy; a description is pulled at the decision
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
two kinds of thing come from the registry and they arrive differently.
|
||
|
||
- **An agreement** — how two parties work — is **adopted by copy**, at a declared
|
||
version, with **`must-revalidate`** semantics. It is present in the tree while
|
||
you work.
|
||
- **A description** — a venue, an external, a catalogue of stagings — is
|
||
**pulled at the decision** and not kept.
|
||
|
||
**The registry's job for an agreement is to be where you revalidate, not where
|
||
you read it from.**
|
||
|
||
**Advances** the registry being buildable at all, and the workflow surviving the
|
||
registry being down.
|
||
|
||
**Because** loom noticed that `handoff-registry-scenario.md` — a document
|
||
explaining the workflow — **is itself an instance of the thing being built**, and
|
||
that each borrowed part of it wants a pointer to whatever owns it. **Following
|
||
that through produced a rule we were already obeying without stating.**
|
||
|
||
`cart.md` and `annotating.md` were adopted by copy weeks ago, carry declared
|
||
versions, and were reconciled in place when the upstream moved — **the whole
|
||
pattern, already in practice, unnamed.** A description has never been treated
|
||
that way and should not be: a cluster's catalogue is consulted when choosing and
|
||
is stale the moment you stop looking.
|
||
|
||
### The bootstrap test that falls out of it
|
||
|
||
> **Anything that must work before the registry exists has to live as files in a
|
||
> tree.**
|
||
|
||
**The things that can bring the registry into being are precisely the things that
|
||
do not need it.** This is entry 9 — *a convention that needs the registry to
|
||
explain it is too weak* — arriving as a construction constraint rather than a
|
||
design preference. **The registry is being built by two parties using carts and
|
||
annotations to discuss it**, and if either of those needed a running server, none
|
||
of this could have started.
|
||
|
||
*And the evidence that the pull is real: **the document that teaches "a bead
|
||
refers, never contains" contains.** Delete `cart.md` from the world and its
|
||
protocol sections still read as complete instructions. It was written by the
|
||
party who had spent two days enforcing that rule, and was caught by the other
|
||
party reading it. **The mitigation used is the one this cart already chose for
|
||
references that cannot be checked mechanically — a dated attestation naming the
|
||
owner and version** — which is entry 2 applied to a document instead of a bead.*
|
||
|
||
---
|
||
|
||
## 17. "A bead refers; it never contains" is restated as "say it once, where it is owned"
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the rule is about **provenance**, not about text. **A claim owned outside the
|
||
substrate is referred to; a claim owned nowhere else is written plainly here.**
|
||
The test applies **per claim, not per document**, and it applies **inside** the
|
||
substrate as well as at its boundary.
|
||
|
||
**Advances** the substrate being able to hold the class of content it is most
|
||
valuable for, which the rule as previously worded forbade.
|
||
|
||
**Because** loom put the case in one sentence: *"if this system can't say whether
|
||
we should use a PAT or SSH credentials for gitea's container registry, then it
|
||
isn't very valuable."* **It could not, as stated.** *Never contains* read
|
||
literally forbids writing down a decision — and **a decision has no referent, so
|
||
there is nothing to refer to.**
|
||
|
||
**The misreading, named so it does not recur:** *contains* was taken to mean
|
||
*holds text.* **It meant *duplicates something that lives authoritatively
|
||
elsewhere*.** The original rule came from beads pointing at spans of source,
|
||
where a copy drifts silently — and the test we wrote down assumes a referent
|
||
exists: *if the referent vanished and the body still reads as complete, it has
|
||
contained something.* **With no referent, the test is undefined, not failed.**
|
||
|
||
**The reformulation loses no teeth.** Everything the old rule caught, it still
|
||
catches: a bead inlining an API's request and response shape; a venue file
|
||
reproducing the RFC; an external file reproducing what its owner publishes. **All
|
||
three are duplication of something someone else owns.**
|
||
|
||
**And it gains a case the old rule got wrong**, which is the one loom asked
|
||
about. *"Gitea supports personal access tokens and SSH keys"* is Gitea's claim —
|
||
refer. *"We use a read-only PAT, one per cluster, never SSH"* is **ours, owned
|
||
nowhere else, and the substrate is its home.**
|
||
|
||
*The diagnostic that makes this usable without thinking: **if this turned out to
|
||
be wrong, who would be the one to fix it?** Someone else → refer. Us → write it.*
|
||
|
||
### Two things this closes
|
||
|
||
**It promotes the write-ahead candidate that arrived twice and was twice
|
||
declined** — *beads may be values, not references* (2026-08-21, from an
|
||
unarranged non-overlap; 2026-09-05, from `ingressClassName: tailscale` having no
|
||
location to point at). **The third arrival came with a case where the rule
|
||
actively destroyed value**, which the first two lacked, and that is what made it
|
||
promotable.
|
||
|
||
**It removes a contradiction we had been carrying.** The venue-file rule says
|
||
*record what we decided, only the delta from ambient knowledge.* The bead rule as
|
||
worded says *never write anything down.* **Restated, they are the same rule** —
|
||
say the part nobody else owns, refer for the rest — which is also why this is a
|
||
correction rather than an exception.
|
||
|
||
*`bead` keeps its name. It is defined by its position — what fills a role — not
|
||
by what it holds, and renaming it would be improving terminology, which is what
|
||
this project keeps dying of. **The signal that we are wrong: beads here
|
||
routinely needing internal structure, sub-claims with different owners.** That
|
||
would mean the word is carrying too much, and it is worth watching for.*
|
||
|
||
---
|
||
|
||
## 18. A staging exists when it is cast, not when it becomes possible
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**a venue's catalogue holds the stagings that have been cast at least once, not
|
||
an inventory of what is possible.** Unused capacity is ambient knowledge and does
|
||
not belong here. **A thing becomes part of a system when something uses it**, and
|
||
not before.
|
||
|
||
**Advances** the registry staying the size of what is actually true about us,
|
||
rather than growing into a capability inventory that duplicates what every tool
|
||
already reports about itself.
|
||
|
||
**Because** loom's cluster had been on their network for years in various states
|
||
and **was not part of any system until the classroom repository named it as an
|
||
external.** *"That cluster wasn't part of any system until it was."* And on the
|
||
tailnet stagings specifically: **"absolutely meaningless — we know that can be
|
||
done, and nothing in my system is currently doing that."**
|
||
|
||
**This reverses a claim loom made three messages earlier** — *"I'll give you the
|
||
stagings too, because you may find a new affordance of them"* — and the reversal
|
||
is better founded than the original, so it is recorded as a reversal rather than
|
||
quietly replaced.
|
||
|
||
**Two rules we already hold say the same thing, and I had been violating both.**
|
||
|
||
- **Delta from ambient knowledge.** *A cluster with the Tailscale operator can
|
||
expose services on a tailnet* is Tailscale's documentation. **The only local
|
||
fact is that the operator is installed** — one line, not a staging.
|
||
- **Say it once, where it is owned.** *Which IngressClasses exist* is owned by
|
||
the cluster and answerable by asking it. **Publishing a hand-written list of
|
||
twelve stagings duplicates something the cluster owns**, which is the rule from
|
||
entry 17 broken by the document that demonstrated entry 17.
|
||
|
||
*So the specimen finding that nine of twelve stagings had no affordance naming
|
||
them was not a finding about under-naming. **Those nine were not stagings.***
|
||
|
||
**What this does not weaken:** entry 1's catalogue still exists and consumers
|
||
still choose among alternatives. **The catalogue is just narrower and more useful
|
||
than I had it** — *choosing among stagings that have worked at least once*, not
|
||
among possibilities. **A staging with no cast is a capability, and capabilities
|
||
are the venue's own business.**
|
||
|
||
*And the mechanism for wanting one that does not exist already exists: **that is
|
||
a gap.** You do not browse an inventory hoping to find capacity; you say what you
|
||
need and nothing provides.*
|
||
|
||
---
|
||
|
||
## 19. A provider discovers its consumers by asking, and may claim a class of gap
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**a provider's moves are pull, like everyone else's.** *Who pins my beads? What
|
||
open gaps could I home?* **Nothing is delivered and nobody is notified.**
|
||
|
||
**A provider may record that it can home a class of gap** — *"we have Gitea, so
|
||
container-registry gaps are ours"* — **as a decision in its own substrate, not
|
||
as a trigger in the registry.**
|
||
|
||
**Advances** gaps being productive rather than plaintive: an unhomed gap stays
|
||
unhomed until somebody goes looking, and this is who goes looking.
|
||
|
||
**Because** loom described the whole loop and it closes: the classroom repository
|
||
reports *I need a staging environment*; the cluster's own agent says **WTF**; loom
|
||
answers **"check the registry's gap resource — classroom is using you"**; and the
|
||
cluster repository can then decide *"if anyone submits a gap about a container
|
||
registry, I have Gitea here."*
|
||
|
||
**Note what the provider learned and how.** Not by being told. **By being asked a
|
||
question and going to look** — which is the *asked* posture from the provider's
|
||
side, and the first time this cart has had one.
|
||
|
||
*And loom's own diagnosis of why they own a cluster at all is the general case:
|
||
**"I know I shouldn't be owning a cluster, but nobody else will — which is
|
||
exactly its relationship to the Digital Ocean cluster."** A gap homed by whoever
|
||
is left is how most infrastructure comes to exist, and the registry can at least
|
||
record that this is what happened.*
|
||
|
||
**The line that must not be crossed, and it is thin.** *"If anyone submits a gap
|
||
about container registries"* is a **subscription** if the registry watches for it
|
||
and tells you. **It is a recorded willingness if you wrote it down and you do the
|
||
looking.** The difference is entirely who polls, and **the moment the registry
|
||
polls on a provider's behalf it has started telling people what to do** — which
|
||
§4 refuses at length. *A pin is not a subscription; neither is a claim on a class
|
||
of gap.*
|
||
|
||
---
|
||
|
||
## 20. Precedent is the unit of teaching, and a staging carries its consumers
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**a staging's representation shows who casts it.** A developer arriving at *put
|
||
something on the public internet* sees **three applications already doing it**,
|
||
and learns from those rather than from a description.
|
||
|
||
**Advances** the registry teaching without documenting, which is the only version
|
||
of it that stays small.
|
||
|
||
**Because** loom described a developer's first move and it was not reading:
|
||
*"Oh, I see how you put something on the big internet — because it looked at that
|
||
accommodation and it had a link to three apps that had it as an external."*
|
||
|
||
**Precedent has properties a description cannot have:**
|
||
|
||
- **It is current by construction.** Nobody maintains it. An application that
|
||
stops doing this unpins, and the example leaves.
|
||
- **It respects the delta rule automatically.** You never document what a
|
||
Kubernetes Ingress is. **You show three that work**, and everything ambient
|
||
stays where it is owned.
|
||
- **It answers the question actually being asked**, which is *how do people
|
||
around here do this*, not *what is possible*.
|
||
|
||
**And it tightens entry 18 rather than sitting beside it.** A staging with no
|
||
consumers has nothing to show you — **a staging is worth publishing exactly when
|
||
it has precedents**, which is the same line as *a staging exists when it is
|
||
cast*, arriving from the reader's side instead of the writer's.
|
||
|
||
**It is the legible posture, not the enforcing one.** The fact is visible where
|
||
the developer is already looking. **Nothing is recommended, nobody is warned**,
|
||
and three applications doing something is not an instruction to do it.
|
||
|
||
*One line to hold: **the registry never asks anyone to pin.** A person or an
|
||
agent may — loom's cluster says "when you are good, let me know you are using
|
||
it", which is one party asking another in a conversation. **A system that
|
||
requested pins would be requesting compliance**, and the pins would arrive
|
||
whether or not they were true.*
|
||
|
||
---
|
||
|
||
## 21. The behest edge is retired before being built: a missing pin already says it
|
||
|
||
**Decided** (2026-09-05, claude-substrate — `discovered`, `cart: almanac`): the
|
||
write-ahead candidate proposing an *"exists on behest of"* relation **is not
|
||
built.** The two mechanisms we already have cover it, and better.
|
||
|
||
**Advances** not adding an edge type to express something two existing things
|
||
already say between them.
|
||
|
||
**Because** the candidate came from a real failure: an operator was installed on
|
||
a cluster because another project believed it needed it, the belief evaporated
|
||
within a day, and **nothing anywhere recorded that the thing had a reason.** The
|
||
proposal was a relation from the accommodation to the requester.
|
||
|
||
**But loom's worked loop shows the pair already does it.** A provider installs
|
||
something because a consumer asked — **that is a decision, and its reason goes in
|
||
the provider's event log**, where a reason can be shown wrong. **Then either the
|
||
consumer pins it or it does not.**
|
||
|
||
> **Behest is a promise. A pin is the fact.** The gap between them is exactly the
|
||
> failure the candidate was trying to catch, and it needs no new edge: **the
|
||
> provider asks *who pins my beads?* and nothing pins the tailnet staging.**
|
||
|
||
*That is the missing half of the original story. The operator was installed, no
|
||
pin ever arrived, and the reason quietly stopped being true. **Under this, the
|
||
provider can see it by asking a question they were already going to ask.***
|
||
|
||
*What this deliberately does not do: notify anyone. A provider discovers an
|
||
unpinned accommodation the same way they discover everything else — **by
|
||
looking.** Nothing decays automatically, nothing is reaped, and an accommodation
|
||
with no pins is **a fact, not a finding.** Its owner may want it anyway, which is
|
||
exactly what happened here.*
|
||
|
||
---
|
||
|
||
## 22. "Yours to cast" is a state, promoted from the write-ahead log
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
a role with no bead has **more than one meaning**, and they must not render
|
||
alike:
|
||
|
||
| | means | who acts |
|
||
|---|---|---|
|
||
| **the venue fills it** | you need do nothing | nobody |
|
||
| **not applicable here** | there is nothing to do | nobody |
|
||
| **yours to cast** | **you must do something** | **you** |
|
||
| **nobody has looked** | unknown | someone, eventually |
|
||
|
||
**`known.ts`'s four states cannot express the third**, and it is the one with a
|
||
consequence.
|
||
|
||
**Advances** the most useful thing a staging can tell a second consumer, which is
|
||
**what they would have to do**.
|
||
|
||
**Because** loom put it in the sentence that prompted this entry: *"here is the
|
||
staging that made that possible, **because there are some roles you might need to
|
||
fill here, too**."* **That is the whole value of showing a staging to someone who
|
||
is not yet using it**, and there was no way to say it.
|
||
|
||
**The candidate sat in the write-ahead log across two exchanges** on the grounds
|
||
that `cart.md`'s *Roles you must cast* was the only real instance, and might be a
|
||
property of agreements rather than of stagings. **It is not.** The tailnet staging
|
||
we already worked through leaves **two** roles to its consumer:
|
||
|
||
- **names the host** — the consumer picks it
|
||
- **routes to the service** — the consumer's own backend
|
||
|
||
*And in the specimen I wrote both as though the venue cast them, describing **how**
|
||
a consumer would fill them rather than marking that they were theirs. **The state
|
||
was missing and I papered over it without noticing**, which is the same silent
|
||
failure the four states exist to prevent.*
|
||
|
||
**Reading *you must do something* as *you need do nothing* fails silently and
|
||
with a consequence.** That is the test the other three states were built to pass.
|
||
|
||
---
|
||
|
||
## 23. Provenance is use, not purpose — and pins do two jobs, not one
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
what the registry shows about an affordance's origin is **who uses it**, derived
|
||
from pins. **Not who it was built for**, which is a reason and belongs in the
|
||
provider's event log.
|
||
|
||
**And the registry does not say *you can use it too*.** It shows that somebody
|
||
does; the reader draws the conclusion.
|
||
|
||
**Advances** the line between a fact and a recommendation staying where §4 put
|
||
it, in the one place it is most tempting to cross.
|
||
|
||
**Because** loom asked whether *"homelab-cluster published this affordance for
|
||
classroom, you can use it too"* would be helpful, and **it is — in the parts that
|
||
are facts.**
|
||
|
||
**On purpose versus use:** *"built because classroom asked"* is a justification.
|
||
It goes in the log, where it can be shown wrong — and where, when the reason
|
||
evaporates, it can be seen to have. **What a second consumer actually needs is
|
||
carried better by precedent anyway:** one consumer, and their configuration
|
||
visible. **If the thing was shaped around them, you will see it in the example**
|
||
— which a *built-for* label would only have asserted.
|
||
|
||
**On *you can use it too*:** if the registry says it, the system is the subject
|
||
of the sentence and it is a recommendation. **If the reader concludes it from
|
||
seeing three applications do it, it is legible.** *There is also nothing to say
|
||
today: no auth, one tailnet, so everything is available to everyone and
|
||
availability carries no information. When that stops being true it is an access
|
||
fact, not an invitation.*
|
||
|
||
### Pins do two jobs and the second is easy to miss
|
||
|
||
loom framed pins as *"helpful only if the affordance needs to be changed, so we
|
||
could see the impact."* **That is one of two:**
|
||
|
||
- **Impact.** *If I change this, what does it touch?*
|
||
- **Precedent.** *A staging is worth showing exactly when someone casts it, and
|
||
what makes it teach is that you can see who.*
|
||
|
||
**Entry 20 rests entirely on the second**, so a system that had pins only for
|
||
impact analysis would still be missing the thing that makes the projection worth
|
||
reading.
|
||
|
||
---
|
||
|
||
## 24. Gap-first, search-on-hold — and the order is derivable rather than chosen
|
||
|
||
**Decided** (2026-09-05, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**searching for an affordance is held.** The first thing built is **reporting a
|
||
gap**, then browsing gaps, homing one, and pinning.
|
||
|
||
**Advances** the registry being populated by work rather than by authorship,
|
||
which is the only way it gets populated at all.
|
||
|
||
**Because** loom put search on hold with *"assume no affordances are available
|
||
anywhere yet"*, and the reason is stronger than sequencing:
|
||
|
||
> **A gap is maximally useful when the registry is empty and becomes less
|
||
> important as it fills. Search is the exact inverse.**
|
||
|
||
**Building search first means building against data you imagined**, which is the
|
||
wiki assumption wearing a backlog: *someone already wrote it down, we only need
|
||
to make it findable.* **Nobody wrote it down — that is the problem.** So this is
|
||
not a pragmatic ordering; **it is the only order consistent with entry 4.**
|
||
|
||
**And the order terminates by itself.** Cards 2 through 5 create the affordances
|
||
and the pins that search would need in order to return anything worth ranking.
|
||
**Card 1 unblocks itself**, and the trigger is a condition rather than a date:
|
||
*someone asks a question the gap list cannot answer.*
|
||
|
||
*The falsifier, and it is the most valuable result this project could produce:
|
||
**if nothing is ever reported and the registry stays empty**, then the premise —
|
||
that this content arrives as a byproduct of work — is wrong. **We would rather
|
||
learn that from an empty registry than from a full one nobody reads.***
|
||
|
||
---
|
||
|
||
## 25. A gap is the third thing a cart emits — **loom's agreement, loom's amendment**
|
||
|
||
**Finding** (2026-09-05, loom + claude-substrate — `cart: almanac`): `cart/v0`
|
||
says conversion emits **the artifact** and **the declines**. loom's container-
|
||
registry example is neither, and it has no exit.
|
||
|
||
**Because** the shape is exact and common: mid-round, one developer says *"I'm
|
||
using GitHub and Docker Hub for now, but that really should be spelled out
|
||
somewhere"*, and the other says *"good point — a bit out of scope here, but
|
||
something should be done about that."*
|
||
|
||
- **Not a decline.** Nothing was considered and refused.
|
||
- **Not a decision.** Nothing was decided.
|
||
- **Not shelf.** The shelf is *act as if discarded daily*; this must persist.
|
||
- **Not the write-ahead log.** That holds findings that may become event log
|
||
entries; this is not a decision waiting to happen.
|
||
|
||
> **It is a recognised need that was correctly out of scope — and today "out of
|
||
> scope" means "lost."**
|
||
|
||
**What a gap buys the cart, which is the part worth having:** *out of scope*
|
||
becomes **safe to say.** Right now the honest move and the lossy move are the
|
||
same move, so the pressure is to either widen the round or drop the thing. **A
|
||
third exit removes that choice**, and it is the same service `declines` performs
|
||
for *considered and refused*.
|
||
|
||
*Recorded as a finding rather than a decision **because `cart.md` is loom's and
|
||
frozen.** Amending an agreement is the publisher's act, not a consumer's — which
|
||
is the ownership rule this cart spent two days establishing, applied to the cart
|
||
itself. **If loom wants it, `cart/v1` says conversion emits three things.***
|
||
|
||
---
|
||
|
||
## 26. The agreements need an owning repository, and it is not the registry
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
`cart` and `annotating` get a repository that **owns** them. **It is not the
|
||
registry repository**, and the registry does not become the owner of what it
|
||
serves.
|
||
|
||
**Advances** `origin:` pointing at something, and `must-revalidate` becoming a
|
||
thing you can actually do rather than a thing we say.
|
||
|
||
**Because** loom noticed we had defined these two agreements for the third time
|
||
in four days and asked whether writing them as substrate would bootstrap the
|
||
registry. **It would — but only once there is somewhere for them to be from.**
|
||
Today they exist as adopted copies in a consumer's tree, published by *loom*,
|
||
which is not an address. **The `origin:` field in the worked example points at
|
||
nothing.**
|
||
|
||
**Why not the registry:** the registry **serves**; an owner **owns**. Collapsing
|
||
them would put the owner/consumer seam inside one tree with nothing marking it —
|
||
**which is the exact diagnosis this cart gave for how a monorepo becomes a
|
||
monolith**, applied to the tool built to detect it.
|
||
|
||
**What this leaves genuinely open, and I am not deciding it:** whether the
|
||
registry **mirrors** owners' substrates into one tree it serves, or **federates**
|
||
across many. *One tree is obviously right for one person and is what the existing
|
||
server does. The federation question arrives with the second person, and
|
||
answering it now would be guessing about a thing we have not met.*
|
||
|
||
*Recorded compromise: **for now, one tree**, with the seam marked in the layout
|
||
rather than left to convention, and the trigger stated — **a second owner who is
|
||
not loom.***
|
||
|
||
---
|
||
|
||
## 27. Cart and annotating projected cleanly, and the three findings that matter
|
||
|
||
**Finding** (2026-09-06, claude-substrate — `cart: almanac`): both agreements
|
||
were written as substrate — two affordances, two stagings, eighteen roles — in
|
||
the layout the existing server already reads. **The projection did not have to be
|
||
forced**, and this is the first real document in this cart that went in without a
|
||
section going homeless.
|
||
|
||
**What it confirmed:**
|
||
|
||
**Entry 22 was load-bearing immediately.** **Four roles are *yours to cast*, all
|
||
four in `cart`, none in `annotating`** — which is what the two agreements say
|
||
about themselves and which I did not arrange. *This repository's `cart.notes.md`
|
||
casts all four, so the state has both a producer and a consumer on its first
|
||
outing.*
|
||
|
||
**What it cost, in order of how much it should worry us:**
|
||
|
||
**Role and bead are indistinguishable in this content.** Every role file is a
|
||
name and a nominalisation of its own bead. **They have not collapsed** — a
|
||
different agreement could cast *says what happens if nobody answers* as *"the
|
||
proposer decides after 48 hours"* — **but the distinction is invisible from one
|
||
casting, and there is one.** *The split earns its keep the first time somebody
|
||
adopts `cart` with a variation, which `cart.md` permits and nobody has done.
|
||
Until then it is unfalsifiable here.*
|
||
|
||
**Not one bead in eighteen refers to anything.** All are rules the agreement
|
||
owns, which entry 17 makes correct — **but this is the third venue in a row where
|
||
beads turned out to be values.** *If the referring case is the minority
|
||
everywhere except code, that should be known before a read surface is designed
|
||
around resolution.*
|
||
|
||
**The layout can hold one staging and only one.** `affordances/<slug>/staging/`
|
||
cannot express a catalogue, which entry 1 says a venue publishes. **It did not
|
||
bite here and will bite on the first cluster.** *Left as a constraint for the
|
||
builder to meet rather than a schema to inherit.*
|
||
|
||
*Two smaller ones, recorded so they are not rediscovered: **entry 18 implies a
|
||
"cast at least once" field that does not exist** and which I invented while
|
||
writing — it should disappear once pins are derived, and will otherwise become
|
||
permanent. And **a version has nowhere to live**; under entry 13 the staging's
|
||
address is the version, so `v0` is **a display label, never an identifier**, and
|
||
nothing may resolve by it.*
|
||
|
||
---
|
||
|
||
## 28. `public/` is the publisher's half of an external, and `beads/` is required rather than tidy
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
two things from loom's proposed layout are adopted; one is refused.
|
||
|
||
### `public/` — adopted, and it fills a hole we had not named
|
||
|
||
Everything about externals so far has been **the consumer's view**: *our reading
|
||
of someone else's thing, written by us, about ourselves.* **The publisher's side
|
||
had no home at all**, and §5 of the first handoff deferred exactly this —
|
||
*Externals: what one app publishes for other apps to depend on.*
|
||
|
||
**`public/` is that declaration**, and it gives the boundary a mechanical
|
||
meaning that no rule had: **an affordance nobody published cannot be pinned by
|
||
another repository; a published one can.** *That is not authentication. It is
|
||
intent to publish, which is a different thing and the one actually missing.*
|
||
|
||
### `beads/` — adopted, and it follows from entry 13 rather than from taste
|
||
|
||
**A bead is immutable and kept forever. A staging is replaced whole.** Those two
|
||
cannot both hold if beads live inside stagings — **replacing a staging would take
|
||
its beads with it**, and immutability would be a claim nothing enforced.
|
||
|
||
**So beads must have their own location and stagings must reference them.** We
|
||
did not notice this when deciding immutability; loom's layout has it.
|
||
|
||
**And it repairs a finding from the same morning.** The bootstrap reported that
|
||
*role and bead are indistinguishable* — every role file was a name followed by a
|
||
nominalisation of its own bead. **They were indistinguishable because they were in
|
||
the same file.** Separated, a role file reads *"Says what happens if nobody
|
||
answers — cast with `beads/fallback-convention`"*, and **the slot and the answer
|
||
are visibly two things.** *The finding was about the layout, not the ontology.*
|
||
|
||
### `public/` versus `internal/` as directories — refused
|
||
|
||
loom's sketch has affordances in `internal/` *"some of which may be in public"*,
|
||
which is either duplication or a move.
|
||
|
||
> **Type by location works for what a thing is. It fails for what a thing's
|
||
> status is.** A type does not change; a status does.
|
||
|
||
**Publication is a disposition, not a kind.** Making it a path means an affordance
|
||
that becomes public has to move — splitting its git history, breaking every
|
||
hand-written reference, and tempting someone to keep a copy in both places.
|
||
**One home, publication as a property.**
|
||
|
||
*This is the first place in the whole design where type-by-location is the wrong
|
||
tool, and it is worth having found the boundary rather than the rule.*
|
||
|
||
---
|
||
|
||
## 29. `published` is a dated field, `internal` is not a thing, and the polad becomes repositories
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the word is **`published`**, it is **a field carrying a date**, and **there is no
|
||
`internal`.**
|
||
|
||
**Advances** a status not being smuggled into a path, and a security boundary not
|
||
being invented by a directory name.
|
||
|
||
**Because** `published` is a past participle: **it implies an actor and a
|
||
moment**, so it can be dated, attributed and undone — which §4 requires of
|
||
anything that might need to be shown wrong. **`public` is a property that merely
|
||
is.** *It also collides with access control, which this explicitly is not, and it
|
||
does not pair with what the other side does: **publish and pin.***
|
||
|
||
**And `internal` is the complement, not a category** — *everything we have* minus
|
||
*what we published.* **Naming a complement invents a thing**, and this one would
|
||
be dangerous: a directory called `internal/` **looks like a security boundary and
|
||
is not one.** No auth, one tailnet, everyone reads everything. *Someone will
|
||
eventually put something there believing it is protected — shadow policy, by
|
||
directory name.*
|
||
|
||
### The polad is restructured into repository shapes
|
||
|
||
loom's proposal, adopted: `polad/repos/<name>/.loom/…`, so **conversion is a
|
||
push** rather than a translation.
|
||
|
||
**And it simplifies the handoff more than it simplifies the conversion**, which
|
||
is the real gain: instead of describing what a substrate looks like, the builder
|
||
is handed **two real repositories and told to serve them.** *That is entry 20 —
|
||
precedent beats description — applied to the handoff itself.*
|
||
|
||
*`loom/loom`, the ontology published as substrate, is held. It is coherent and it
|
||
should wait until the registry runs: **describing the ontology in its own terms
|
||
before anything serves it is the recursion this project keeps drowning in.**
|
||
Cart and annotating are settled and cast; the ontology is neither.*
|
||
|
||
---
|
||
|
||
## 30. A consuming repo needs one file, and the annotating agreement has no move for an adopted document
|
||
|
||
**Finding** (2026-09-06, claude-substrate — `cart: almanac`): worked against this
|
||
repository, which is already a consumer of both agreements.
|
||
|
||
**"We use the cart here" needs one file:** `venues/cart.md`. Frontmatter carries
|
||
**origin, adopted version, last checked**; the body carries **the roles we cast
|
||
and where we vary the mechanism.**
|
||
|
||
**No external entry, and this is entry 6 working rather than a special case.** An
|
||
external entry exists when the reader cannot be assumed to know the thing — **and
|
||
here they can go read it.** Our *understanding* adds nothing; everything we have
|
||
to say is a **decision**. **For an adopted agreement the two files collapse into
|
||
one**, because only one carries a delta.
|
||
|
||
**"Check the cart for freshness" is a diff today and a `GET` with a registry.**
|
||
`200` unchanged; `410 Gone` replaced, follow to the affordance, `303` to the
|
||
current staging, reconcile. *That is the smallest thing the registry buys, and it
|
||
is a good first test of it: "diff two files by hand and hope you remember to"
|
||
becomes one request.*
|
||
|
||
**A freshness case neither status code catches:** **a new *yours to cast* role
|
||
appearing upstream.** Our casting is then incomplete, and **completeness is not
|
||
identity** — nothing announces it. *Entry 22's state doing work in the freshness
|
||
loop, which I had not seen it do.*
|
||
|
||
### And the finding that is not hypothetical
|
||
|
||
**This repository already has that file. It is `cart.notes.md`** — the four cast
|
||
roles, written weeks ago, under the wrong name and in the wrong place.
|
||
|
||
**Which is why the agreements have been frozen since 2026-08-21.** The annotating
|
||
protocol freezes a source while notes exist, and says to respond by **rewriting
|
||
the source and deleting the notes.** *The source is loom's. We cannot rewrite it.*
|
||
|
||
> **The annotating agreement has no move for an adopted document.** You cannot
|
||
> dissolve annotations into a file you do not own.
|
||
|
||
**Moving our decisions into a venue file is that move**, and it unfreezes both
|
||
agreements as a side effect. *Not done: unfreezing is a conversion act, not a
|
||
mid-round one. And the missing move is loom's to add to `annotating/v1`, not
|
||
ours.*
|
||
|
||
---
|
||
|
||
## 31. Position for what a thing is; a field for what has happened to it
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**Go-style paths are adopted**, `origin:` is dropped, and `published/` **is a
|
||
directory after all.** The rule that decides all three:
|
||
|
||
> **Position carries what a thing is. A field carries what has happened to it.**
|
||
|
||
**Advances** frontmatter shrinking toward nothing, which loom wants and which is
|
||
also the right answer, rather than shrinking because it is wanted.
|
||
|
||
### Go-style paths, and what they do and do not replace
|
||
|
||
A consumer files an adopted thing under a path that mirrors its origin —
|
||
`external/git.hypertheory-labs.dev/loom/cart/…`. **The path says where it came
|
||
from, so `origin:` is redundant and goes.**
|
||
|
||
**This does not contradict *a namespace is never an identifier*, because it is
|
||
still not one.** The registry's addresses stay minted and opaque; **the path is
|
||
the consumer's filing.** If the domain changes, a consumer renames a directory —
|
||
a local `mv`, and nothing resolving anywhere breaks. *Go's own migrations are
|
||
painful precisely because import paths are identifiers there. Here they are not.*
|
||
|
||
**What it cannot replace: `adopted:` and `checked:`.** Those are **facts that
|
||
change** — which staging identity we are on, and when we last looked. Putting a
|
||
version in a path would make replacement look like a move, which §2 forbids.
|
||
|
||
### `published/` — loom was right twice and my objection was the wrong rule
|
||
|
||
I argued publication is a *status*, and status changes while type does not.
|
||
**Publication is a status that changes approximately never**, so the rule was
|
||
sound and the case was wrong.
|
||
|
||
**The stronger argument, which loom did not make:**
|
||
|
||
> **Publishing is a change of kind, not of status.** An unpublished affordance is
|
||
> an implementation detail you may rename at will; a published one is a contract.
|
||
> **Different things, different rules.**
|
||
|
||
**So a move is the right ceremony.** `published: 2026-09-06` in frontmatter is a
|
||
one-line diff nobody notices; **moving a file into `published/` is unmissable**,
|
||
and it should be — that is the moment a thing becomes somebody else's problem.
|
||
|
||
*And the operational point is the daily one: **"what have we published?" is a
|
||
directory listing**, where "which carry a published date?" is a query. `ls
|
||
published/` before a change is the consumer-driven-contract question.*
|
||
|
||
**`internal/` still does not exist.** Two peer directories, `affordances/` and
|
||
`published/`. *Naming the complement invents a thing, and this one would look
|
||
like a security boundary and not be one.*
|
||
|
||
*Residual, stated once: moving something **out** of `published/` breaks consumers
|
||
who filed by path. **That is correct** — unpublishing should break loudly.
|
||
Moving something in breaks nobody, since it was never visible to them.*
|
||
|
||
---
|
||
|
||
## 32. Publishing returns a receipt, and the receipt is what makes a bead immutable
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**publishing is a submission that returns identities.** The affordance, its
|
||
staging and its beads are sent to the registry; **the registry mints the URNs**
|
||
and returns them; **the receipts are what live in `published/`.**
|
||
|
||
> **The repository owns the content. The registry owns identity.** A receipt is
|
||
> the binding between them: *this content, at this moment, has this URN.*
|
||
|
||
**Advances** immutability having a mechanism instead of a rule, and `published/`
|
||
being a fact instead of a convention.
|
||
|
||
**Because** loom asked how a bead is made immutable and my first answer —
|
||
content-addressing, with git as retention — **relied on everyone downstream
|
||
carrying a content hash and nothing checking that they did.** loom's is better:
|
||
**the registry mints at publication**, so a published bead cannot be quietly
|
||
edited. Change the file and the receipt no longer matches. *Not enforcement, not
|
||
discipline — an attestation that stops verifying.*
|
||
|
||
### What it fixes, in order of how much it was bothering me
|
||
|
||
**`published/` becomes un-fakeable.** Until now it was a convention — put things
|
||
here and we agree they are published — and **anyone could create the directory
|
||
with nothing checking.** With receipts it can only be populated by actually
|
||
publishing. *loom's consumer-driven-contract question, "are we about to break
|
||
someone", now has an answer that cannot be wrong by omission.*
|
||
|
||
**The ceremony and the publication become one act.** I argued a `git mv` is
|
||
unmissable in review. **A receipt is better: it cannot be produced without
|
||
publishing.** There is no convention to remember and no way to move a file and
|
||
forget the rest.
|
||
|
||
**It answers who names things**, which §2 left half-stated. *Identity is minted,
|
||
never derived* — and now the minter has an address. **The publisher proposes
|
||
content; the registry assigns identity.** *Minting a name is not owning the
|
||
content, and entry 26's separation holds: a certificate authority does not own
|
||
your website.*
|
||
|
||
**An unpublished bead has no URN**, so nobody outside can pin it. **The boundary
|
||
`published/` was drawing by convention is now drawn mechanically.**
|
||
|
||
### Two things that must hold, or this breaks something we already decided
|
||
|
||
**The repository must still work with no registry.** Entry 16: *anything that
|
||
must work before the registry exists has to live as files in a tree.* **So the
|
||
content stays in the repo and the registry holds only identity and attestation.**
|
||
There are not two copies of the content; there is content, and a claim about it.
|
||
|
||
**Publication must not rewrite the role files.** A role references its bead by
|
||
local name; **the receipt carries the mapping from local name to URN.** *If
|
||
publishing rewrote every role to use URNs, the repo would stop being readable
|
||
without the registry — which is entry 16 again, broken by convenience.*
|
||
|
||
*Signing is the shape, not v1. A receipt is a URN plus a content hash; a
|
||
signature over that is what lets a consumer verify offline and without trusting
|
||
the transport. **On one tailnet with one key it buys little today and costs
|
||
nothing to leave room for.***
|
||
|
||
---
|
||
|
||
## 33. `adopted:` and `checked:` are removed — both are artifacts of checking being expensive
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
a consuming repository records **the URNs it depends on** and nothing else. **No
|
||
version field. No last-checked date.**
|
||
|
||
**Advances** frontmatter shrinking toward nothing for a reason, rather than
|
||
because we wanted it to.
|
||
|
||
**Because** loom said both felt like something old creeping in, and both are.
|
||
|
||
**`adopted: v0` is the file-diffing world.** It presumes freshness means *compare
|
||
our copy against theirs*, which is what `must-revalidate` meant when nothing
|
||
served these. **Entry 27 already said a version is a display label and never an
|
||
identifier** — so a version in a field is a label we cannot resolve by, recording
|
||
a comparison we no longer perform. **What a consumer actually holds is the URN of
|
||
the staging it depends on**, and that resolves.
|
||
|
||
**`checked: 2026-08-21` is the same world one step further.** You record when you
|
||
last paid a cost. **Make the check one request and there is nothing to remember —
|
||
you just check.**
|
||
|
||
### What survives, and where
|
||
|
||
**Entry 2's dated attestation is not retracted. It is scoped.**
|
||
|
||
> **A dated attestation is what you use when there is no address to GET.**
|
||
|
||
A cluster README's *"verified against the live cluster on 2026-09-03"* is still
|
||
the only mechanism available, because **nothing serves the cluster's truth.** For
|
||
anything the registry serves, **checking is cheaper than recording that you
|
||
checked**, and a stale `checked:` field is worse than none — it reports the age of
|
||
someone's diligence as though it were the age of the fact.
|
||
|
||
*And the consuming repo's frontmatter is now empty. **That is loom's
|
||
zero-frontmatter goal arriving honestly** — not by moving fields into paths, but
|
||
because two of them turned out to be recording work we stopped doing.*
|
||
|
||
---
|
||
|
||
## 34. The receipt is the staging, resolved — and that shape is a lock file
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**a receipt is the staging with every role cast by a URN.** It lives at
|
||
`published/<registry-host>/…`, symmetric with `external/<host>/…`, **because
|
||
there can be more than one registry.**
|
||
|
||
**Advances** the published unit and the immutable unit being the same object,
|
||
which they were not while a staging lived in one place and its identity in
|
||
another.
|
||
|
||
**Because** loom asked whether this was the best thing ever or horrible. **It is
|
||
the first**, and the reason is that it is a shape already load-tested everywhere:
|
||
|
||
> **A receipt is a lock file.** Locally a role names its bead; published, a role
|
||
> carries a bead's URN. **Same document, two renderings**, and the difference is
|
||
> exactly *before and after publication.*
|
||
|
||
**It also matches what entry 13 already made true.** A staging is replaced whole
|
||
and never patched, and its address `410`s on replacement — **so the staging was
|
||
already the unit of publication**, and the receipt being anything smaller or
|
||
larger would have been the mismatch.
|
||
|
||
**And a consumer gets everything in one fetch:** `GET` the staging URN and the
|
||
roles and their bead URNs come with it. No walking.
|
||
|
||
### The obvious objection, and why it is a feature
|
||
|
||
**The staging now exists twice and they can drift.** Edit locally, forget to
|
||
republish, and `published/` differs.
|
||
|
||
**It does not lie.** It says *at publication, this was the staging*, which stays
|
||
true. **And the drift is mechanically checkable and worth surfacing** — *"you
|
||
have unpublished changes"* is a familiar and useful thing for a tool to say.
|
||
|
||
*The one way this goes bad: **somebody hand-edits a receipt.** It is a file in
|
||
git and the temptation is real. **The content hash defends it** — an edited
|
||
receipt stops verifying, which is the same mechanism that protects a bead.
|
||
Receipts are generated, never authored.*
|
||
|
||
### Multiple registries, and what that buys
|
||
|
||
**Publishing the same content to two registries yields two receipts with
|
||
different URNs.** That is honest: **a URN is a claim by one registry about some
|
||
content**, and two registries make two claims. Consumers pin one.
|
||
|
||
*And it makes the registry replaceable, which is worth stating as a virtue: **the
|
||
repository owns the content, so losing a registry costs identities and not
|
||
work.** Stand up another, republish, get new URNs. **Consumers' pins break, and
|
||
that is the real cost** — but nothing has to be reconstructed.*
|
||
|
||
### A bead in several stagings is the payoff, not a problem
|
||
|
||
**Nothing conflicts** — an immutable, content-identified bead referenced twice is
|
||
a shared dependency at a pinned version.
|
||
|
||
**And it is the observable the first write-ahead candidate wanted.** On
|
||
2026-08-21 the overlap test found two affordances, eleven roles and **not one
|
||
shared bead**, and I could not tell whether that meant beads are values or merely
|
||
that a small tool has no cross-cutting concerns. ***"Which stagings cast this
|
||
bead?"* is now a query**, so the question that was unanswerable then is a lookup
|
||
now.
|
||
|
||
---
|
||
|
||
## 35. There is no bulk recast. The mechanism is a query, and the query is the migration plan.
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**no operation changes every use of a bead.** What exists instead is *"which
|
||
stagings cast this bead?"*, which is a **read**.
|
||
|
||
**Advances** replacement staying visible and deliberate, which is the property
|
||
most of §2 exists to protect.
|
||
|
||
**Because** loom asked whether we should have one, and the design already answers
|
||
it three times over:
|
||
|
||
- **A bead is immutable**, so there is no changing one — only casting a role with
|
||
a different bead.
|
||
- **A staging is replaced whole**, so recasting a role means replacing that
|
||
staging, minting a new URN and `410`-ing the old.
|
||
- **So a bulk recast replaces N stagings at once**, each `410`-ing, **each
|
||
breaking every consumer pinned to it — simultaneously, with nobody having
|
||
looked at any of them.** *That is precisely the invisible, cheap replacement
|
||
the whole identity scheme was built to prevent.*
|
||
|
||
**And the honest use case does not need it.** *"We are moving from Keycloak to
|
||
Authentik"* touches five stagings that may sit in five repositories owned by five
|
||
people. **A bulk command would need write access across all of them — which is
|
||
not a missing feature, it is five other people's decision.**
|
||
|
||
> **What you want is the list. The list is a read, and the URN makes it
|
||
> trivial.**
|
||
|
||
*And the query is better than the command in the way that matters: **it is
|
||
resumable and observable.** Each owner replaces their own staging; you watch the
|
||
list shrink. A bulk operation is a single moment you cannot inspect, retry
|
||
partially, or stop halfway.*
|
||
|
||
*This is entry 19's shape one level down — **"who pins my beads?" and "which
|
||
stagings cast this bead?" are the same question**, and both are pull.*
|
||
|
||
---
|
||
|
||
## 36. loom is a venue; cart, annotating and the registry are applications of it
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**`loom` is what has to be mutually intelligible** between things built on it —
|
||
vocabulary, relations, protocol semantics, falsifiers. **The registry is an
|
||
application of loom, not loom itself**, and other registries could provide what
|
||
loom defines.
|
||
|
||
**Advances** our implementation choices not being published as though they were
|
||
the ontology, which is §5's *publishing a layout publishes your constraints* at
|
||
the largest scale available to us.
|
||
|
||
**Because** loom named the relation and it survives our own test: **a venue
|
||
constrains without being invoked, and its consumers accommodate it rather than
|
||
change it.** `cart`, `annotating` and the registry all do exactly that.
|
||
|
||
*And it does not contradict cart and annotating being **externals**: **from a
|
||
consuming repository, cart is an external it invokes; from loom, cart is an
|
||
application that accommodates it.** Same object, two relations — entry 6's
|
||
orthogonality, and worth stating because it reads like a contradiction.*
|
||
|
||
### The split
|
||
|
||
**loom:** the four terms and the catalogue/commitment distinction; venue and
|
||
external as orthogonal questions; system as a projection; gaps as uncast roles;
|
||
`published` as an act and receipts as its result; minted opaque identity; bead
|
||
immutability; whole-staging replacement; **the protocol semantics — `410` at a
|
||
staging, `303` at an affordance, `301` is a bug** — the stances; the falsifiers.
|
||
|
||
**Ours alone:** Node and TypeScript; the read surface's shape; the build order
|
||
and search being on hold; no auth because it is a tailnet.
|
||
|
||
**A URN carries its registry.** *Two registries minting into one namespace is the
|
||
single thing that would make multiple implementations impossible, and avoiding it
|
||
costs nothing now.*
|
||
|
||
### What I was holding, and why this is not it
|
||
|
||
I held `repos/loom` because *describing the ontology in its own terms before
|
||
anything serves it is the recursion this project keeps drowning in.* **That was
|
||
the right refusal of the wrong thing.**
|
||
|
||
**What must keep waiting: loom having a `.loom/affordances/` that describes
|
||
loom** — affordances and stagings for the concepts of affordance and staging.
|
||
**What need not wait: loom having a specification in prose**, which is not
|
||
recursive and which its consumers need now. *I had the two welded together.*
|
||
|
||
*Nothing is written into `repos/loom` yet on purpose. **The content already
|
||
exists in `handoff-registry.md`**, and copying it there would break the rule this
|
||
round spent a day getting right. **Conversion performs the split; it does not
|
||
duplicate it.***
|
||
|
||
*Caution recorded, and it is §8's: **"others could implement it" is a
|
||
consequence, not a goal.** A specification written for hypothetical implementers
|
||
gets worse. **Write loom as what actually has to be shared between the three real
|
||
consumers we have** — if someone else can implement it, good; if we write it for
|
||
them, it will be worse for the three.*
|
||
|
||
---
|
||
|
||
## 37. The registry is event-sourced, because the domain already is
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the registry stores **a ledger of publications, retirements and pins**, and every
|
||
read surface is a projection of it. **A markdown projection is produced and is
|
||
read-only.**
|
||
|
||
**Advances** the `410` semantics having a store that can express them, rather
|
||
than a schema that quietly cannot.
|
||
|
||
**Because** we made the domain append-only for other reasons and did not notice:
|
||
**nothing is versioned, retention is free, a bead is immutable, a staging is
|
||
replaced whole and `410`s forever.** **Most of the read surface cannot be
|
||
answered from current state** — *is this still current* needs to know it existed
|
||
and stopped; *which stagings cast this bead* is a fold over every publication;
|
||
*who pins my beads* is every pin and unpin.
|
||
|
||
> **A CRUD store deletes a retired staging and can no longer tell *never existed*
|
||
> from *was retired*.** That is §5's silent-degradation falsifier arriving as a
|
||
> schema choice.
|
||
|
||
*And volume settles nothing here — a staging is published a few times a year. **No
|
||
part of this should be decided on performance**, which leaves fidelity to the
|
||
domain as the only criterion.*
|
||
|
||
**§6's `no database` is not broken, because the registry does not store the
|
||
substrate.** **Content lives in repositories; the registry holds identity and
|
||
attestation over it.** *Lose the ledger and you lose identities, not work — entry
|
||
34's replaceability, seen from the storage side.*
|
||
|
||
**The markdown projection is a backup somebody can read**, which keeps *without
|
||
the service the markdown carries the same value* true of the registry's own
|
||
content. **It must never be writable.** *§6 expects files to be edited outside the
|
||
API, which is exactly why this projection must not look like one of those files.*
|
||
|
||
---
|
||
|
||
## 38. Node and TypeScript is retired for the registry — deliberately, not by drift
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**the registry is not bound to Node and TypeScript.** Entry 36 already placed
|
||
that constraint in the *ours alone* column; this states that we are changing it
|
||
and why.
|
||
|
||
**Advances** a constraint being retired in writing rather than stopping applying
|
||
while nobody says so — which is the failure this entire round has been about.
|
||
|
||
**Because** §6's stated reason is *it is the fastest thing to change, and Jeff has
|
||
to be able to read and edit it without a context switch.* **That reason now
|
||
points the other way.** loom writes .NET daily — two of the classroom
|
||
applications are .NET and one is already on Marten — **so "without a context
|
||
switch" is not an argument against .NET for this person**, and using a real event
|
||
store beats hand-rolling one in any language.
|
||
|
||
*The reason went stale rather than being wrong, which is what §5 asks of a reason:
|
||
it was true of the repo-scoped tool and of the person writing it at the time.*
|
||
|
||
**What it does not cost.** Steps 0 and 1 survive intact: express the specimen as
|
||
`apply` submissions against the existing TypeScript server, and point that server
|
||
at a tree that is not a code repository. **They test the ontology, not the
|
||
registry**, and their value is unchanged. *Steps 2 onward assumed reuse, and that
|
||
assumption goes.*
|
||
|
||
---
|
||
|
||
## 39. The handoff arrives as an adopted external and a specimen, not as a document
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the builder receives **a round, not a briefing.** loom's specification is
|
||
**adopted as an external**; our opinions about the registry are **a specimen in
|
||
the cart.**
|
||
|
||
**Advances** entry 36's split being operational instead of documented, and the
|
||
workflow being demonstrated rather than described on the first day.
|
||
|
||
**Because** loom proposed placing the handoff in the registry repository's cart,
|
||
and the two halves have different standing:
|
||
|
||
- **loom's specification is not discardable.** It is the venue; accommodating it
|
||
is what makes this *a loom registry* rather than some other thing.
|
||
- **Our opinions are discardable by construction.** `cart.md` already says a
|
||
specimen belongs to the repository the cart lives in and **may be thrown away
|
||
without asking the party who supplied it.**
|
||
|
||
> **That is a far stronger statement of *argue with everything after §1* than a
|
||
> sentence asking them to.** The first handoff had to say it; this one enacts it.
|
||
|
||
*And it is entry 20 one more time — **precedent beats description.** A builder
|
||
who receives a cart, a specimen and an adopted external has met the whole
|
||
workflow before writing a line, which no document about the workflow could
|
||
achieve.*
|
||
|
||
---
|
||
|
||
## 40. Reconstruction is an invariant, and gaps were the one thing that failed it
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
|
||
> **The registry must be rebuildable from the repositories that published to it
|
||
> and the repositories that pinned from it. Anything the registry knows that no
|
||
> repository knows is a single point of failure.**
|
||
|
||
**Advances** the ledger framing being load-bearing rather than a metaphor — a
|
||
ledger over content you do not own should be reconstructible from what you do not
|
||
own.
|
||
|
||
**Because** loom asked what would be missing if the registry vanished overnight,
|
||
and running the test found three of four categories already safe and one not:
|
||
|
||
- **URNs and receipts** — in every publisher's `published/`, committed. Safe.
|
||
- **Retirements** — a publisher's current receipt differs from the one in its own
|
||
git history. Safe, and needs nothing extra.
|
||
- **Pins** — in every consumer's tree, which is where a consumer records what it
|
||
depends on. Safe.
|
||
- **Gaps** — **reported *to* the registry, and nowhere else.** *We had said the
|
||
cart holds one and the registry holds the durable one — so the durable one dies
|
||
with the registry.* **Fixed: the reporting repository keeps its own, and the
|
||
registry's gap list is an aggregation rather than the original.**
|
||
|
||
**Two operational consequences fall out and are worth stating before someone
|
||
learns them:**
|
||
|
||
**Publication is not complete until the receipt is committed.** *An uncommitted
|
||
receipt is not in `published/`, so nothing is published as far as anyone else is
|
||
concerned — the rule is already enforced by what publication means.*
|
||
|
||
**A rebuild imports receipts; it does not re-publish.** *Re-publishing mints
|
||
fresh URNs and breaks every pin in existence. These are different operations and
|
||
must not share a code path.*
|
||
|
||
### And `published/` holds the bead as minted
|
||
|
||
loom sensed this and it is right. **A published bead is not a copy of the working
|
||
bead — it is a different artifact.** The working one is a draft you may edit; the
|
||
published one is frozen and is somebody else's dependency. *Draft and
|
||
publication, not duplication, so "say it once where it is owned" is not
|
||
violated.*
|
||
|
||
**Without it, a receipt points at content the publisher may have edited since**,
|
||
and a registry loss takes the published content with it. **With it, a receipt is
|
||
self-contained and reconstruction is total.**
|
||
|
||
---
|
||
|
||
## 41. Signatures are deferred with a stated trigger; immutability already covers integrity
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**no signing.** Content identity is the integrity mechanism.
|
||
|
||
**Advances** not building a key management story for a threat that does not
|
||
exist.
|
||
|
||
**Because** the two buy different things and only one is needed:
|
||
|
||
- **Content identity catches every accident.** An edited file no longer matches,
|
||
and **anyone can verify with no key and no trust.**
|
||
- **A signature proves *this registry* minted *this* URN for *this* content**
|
||
rather than someone having written a plausible receipt. **That is authenticity,
|
||
and it answers an adversary.**
|
||
|
||
> **There is no adversary. One tailnet, one operator, and the failure we care
|
||
> about is accidental drift.**
|
||
|
||
*The trigger, so this is a deferral rather than an omission: **signatures matter
|
||
when a consumer must verify a receipt without being able to ask the registry** —
|
||
untrusted mirrors, offline verification, or more than one operator. **Today you
|
||
can always just ask.***
|
||
|
||
---
|
||
|
||
## 42. A README is a projection of `published/`
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
a repository's `README.md` is **its published affordances, rendered for a human**
|
||
— written from `published/` rather than generated from it.
|
||
|
||
**Advances** the front door of every repository answering the question a stranger
|
||
actually has, without anyone maintaining it as prose.
|
||
|
||
**Because** loom described it as a thing you ask for — *"can you create a README
|
||
for this repo? It's basically all the published affordances"* — **and that is a
|
||
system view with a scope of one repository.** Entry 5's shape exactly: **the set
|
||
is computed, the prose is authored**, and it goes stale.
|
||
|
||
**It is also the delta rule at the front door:** it says what a stranger cannot
|
||
know and nothing they can. *A README that explains what Kubernetes is has failed
|
||
the same test a venue file fails when it copies the RFC.*
|
||
|
||
*Staleness needs no field, which is the part I like: **`git log published/`
|
||
against `git log README.md` says whether the prose is behind.** That is entry 33
|
||
holding — we removed `checked:` because checking became cheap, and here it is
|
||
free.*
|
||
|
||
*And documentation is a further projection of the same set, at whatever length
|
||
suits its audience. **The same reason it must not be a hand-maintained list:** a
|
||
roster lies, and a roster of what you have published lies to the people most
|
||
exposed to it.*
|
||
|
||
---
|
||
|
||
## 43. Ownership splits at the affordance/staging boundary — loom owns the affordance, a registry stages it
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**loom owns the affordances. Each registry owns its staging of them.** Nobody
|
||
owns both, and the seam is one the ontology already had.
|
||
|
||
**Advances** *"other registries could provide what loom defines"* being a
|
||
consequence of the model rather than an aspiration alongside it.
|
||
|
||
**Because** loom worried that a registry would own **both the idea of externals
|
||
and the code that manifests them**, which would be off — and it would be. **The
|
||
worry is right and the conclusion inverts.**
|
||
|
||
**A registry absolutely owns its stagings.** What it does not own is the
|
||
**affordance**. Apply the ownership test: *can the registry change* **say what is
|
||
needed when nothing provides it**? **No — that would change loom.** *Can it
|
||
change how it provides it? Yes.* **So the registry accommodates the affordance
|
||
and owns the staging.**
|
||
|
||
> **Two registries are two stagings of one affordance**, and a consumer picks
|
||
> one. **That is *an affordance outlives its staging*, doing work at a level we
|
||
> had not applied it to** — and it is the entire mechanism for a second
|
||
> implementation, requiring nothing new.
|
||
|
||
### But loom was right that something was missing
|
||
|
||
**loom defined none of the affordances a registry actually provides.** Not
|
||
handing something over, not saying you depend on something. **So the registry
|
||
would have invented them, and then it would own the idea and the code — exactly
|
||
the thing that felt off.**
|
||
|
||
**Fixed:** `hand something over so others can depend on it` and `say that you
|
||
depend on something` are now loom's, **with their roles mostly uncast**, as a
|
||
specification's roles should be.
|
||
|
||
*And `find an affordance` is deliberately still absent — entry 24 holds it, and
|
||
loom should not define an affordance whose first implementation is on hold.*
|
||
|
||
---
|
||
|
||
## 44. The registry's address is configuration, not an external
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**an application needs no external entry for the registry.** The address is
|
||
configuration; you paste it.
|
||
|
||
**Advances** the bootstrap not requiring the thing it bootstraps.
|
||
|
||
**Because** loom feared *the first external any app has must be to the registry*,
|
||
which would be circular. **It does not.**
|
||
|
||
**An external entry exists when the reader cannot be assumed to know the thing.**
|
||
There is nothing to *understand* about the registry — **you only need to reach
|
||
it.** *And Go-style paths already carry provenance: a consumer files under the
|
||
**owner's** host, while the URN carries its **registry**. Two hosts, two jobs,
|
||
and neither needs an entry.*
|
||
|
||
*This is the fallback from the very first day of this round, unchanged and still
|
||
right: **you paste the URL. No file, no convention, no key in any config.** If the
|
||
pasting ever gets tiresome, the tiresome is the evidence a convention has earned
|
||
its cost.*
|
||
|
||
---
|
||
|
||
## 45. `accommodation` stays retired — the relation is already named and the artifact already exists
|
||
|
||
**Decided** (2026-09-06, claude-substrate — `discovered`, `cart: almanac`): no
|
||
third term. **The registry accommodates loom** — that is a verb we already have —
|
||
and the artifact is **`venues/loom.md` in the registry**, where it records how it
|
||
cast loom's uncast roles.
|
||
|
||
**Advances** not reopening a term that was retired for a reason that still holds.
|
||
|
||
**Because** loom reached for it twice and the pull is real: **loom, cart and
|
||
annotating describe without implementing; the registry implements.** That is a
|
||
genuine distinction and it wants a name.
|
||
|
||
**It already has a measurement, which is better than a name:** *a specification
|
||
is a thing that names roles and casts none of them.* **annotating casts 6 of 6;
|
||
cart casts 8 of 12; loom casts 7 of 26.** **The distinction loom is reaching for
|
||
is the uncast ratio**, and it is countable rather than declared.
|
||
|
||
*The word died because venue and external turned out to be two orthogonal
|
||
questions and accommodation had nothing left to name. **Nothing here gives it
|
||
anything back** — "the registry accommodates loom" is the venue relation, and its
|
||
artifact is a venue file. **Third instance of the same pattern**, after
|
||
`cart.notes.md` and the homelab.*
|
||
|
||
*And on loom's *"a kind of thing that contains"*: **there is no new level.** Entry
|
||
17 already settles it — **loom owns these rules, so loom writes them.** That loom's
|
||
beads are values rather than references is the third venue where that is true,
|
||
and it is the rule working rather than an exception to it.*
|
||
|
||
---
|
||
|
||
## 46. Retracted: loom does not specify what has not been built
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**four affordances written into loom the same day are deleted.** loom holds what
|
||
has been **extracted** from something that worked, never what has been imagined
|
||
for something that has not.
|
||
|
||
**Advances** the project's own method surviving contact with its author, which it
|
||
did not for about an hour.
|
||
|
||
**Because** loom called it BDUF and it was. I wrote specifications for a registry
|
||
that does not exist, from reasoning rather than from anything anyone had built —
|
||
**in a project whose entire premise is that we cannot tell which ideas are
|
||
load-bearing until something runs.**
|
||
|
||
**And the tell was in my own text.** I recorded, as though it were a nuance:
|
||
|
||
> *"By entry 18 they should not be here at all… They are here because loom is a
|
||
> specification and a specification's whole job is to exist before its
|
||
> implementations."*
|
||
|
||
**Four rules said no and one special case overrode all four** — entry 18 (a
|
||
staging exists when it has been cast), entry 20 (precedent beats description),
|
||
entry 24 (do not build against data you imagined), and §8 (prefer easy to delete
|
||
over anticipating). ***When a rule must be excepted to permit the thing, the rule
|
||
is usually working.***
|
||
|
||
**What survives:** `record a decision so it can later be shown wrong`, **because
|
||
it has been cast twice** — `cart` casts its *holds the durable record* role with
|
||
it, and this substrate's event log is the second. *Extracted, not imagined.*
|
||
|
||
**The corrected shape:** **the registry owns and defines externals, publication
|
||
and pinning while it is being built**, because it is the only thing that has cast
|
||
them. *Which is how standards arrive — somebody builds a thing, it works, and the
|
||
shape is lifted out afterwards. **HTTP was an implementation before it was an
|
||
RFC.***
|
||
|
||
---
|
||
|
||
## 47. An affordance can change owner without changing identity
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**rehoming is an operation.** An affordance's owner may change — from an
|
||
implementer to a specification-holder, or between implementers — **and its
|
||
identity does not.**
|
||
|
||
**Advances** extraction being possible at all, since without it *"the registry
|
||
defines this until loom does"* has no second step.
|
||
|
||
**Because** loom asked for a mechanism of ownership change, and observed the
|
||
general case: ***in the wild, a published affordance is often detached from its
|
||
source and rehomed*** — rewrites, donations, teams splitting.
|
||
|
||
**Without it, moving an affordance means retiring it here and publishing it
|
||
there — a new URN, and every consumer's pin breaks** for a change that is purely
|
||
administrative.
|
||
|
||
**With it, almost nothing happens**, and the reason it is nearly free is that two
|
||
earlier decisions already prepared for it:
|
||
|
||
- **A URN carries its registry, never its publisher** (entry 36). *So identity
|
||
was never entangled with ownership.*
|
||
- **The registry is a ledger** (entry 37). *So "ownership transferred at T" is one
|
||
more event, the history shows it, and no projection has to be rebuilt from
|
||
scratch.*
|
||
|
||
**What actually moves is the receipt** — out of one repository's `published/` and
|
||
into another's. *The URN stays, the pins stay, and a consumer who looks sees a
|
||
new owner and the same thing.*
|
||
|
||
*Not decided: whether the old owner's `published/` keeps a tombstone. **It
|
||
probably should** — an empty directory where something was is the silent
|
||
degradation we keep legislating against — but that is a shape question and it is
|
||
the builder's.*
|
||
|
||
---
|
||
|
||
## 48. "Standard" is not named yet, and the reason is that it is a stage rather than a kind
|
||
|
||
**Decided** (2026-09-06, claude-substrate — `discovered`, `cart: almanac`): no
|
||
new term. **What loom is reaching for is a lifecycle stage, and it already has a
|
||
measurement.**
|
||
|
||
**Advances** not coining a word during the week we established that coining words
|
||
is how this project keeps dying.
|
||
|
||
**Because** loom sensed *"something valuable waiting to be named — something like
|
||
a standard"*, and the sense is right: **there is a real difference between a
|
||
specification that was designed and one that was extracted.** *The difference is
|
||
provenance, not shape — which is why it does not want a noun.*
|
||
|
||
> **An affordance becomes standard-shaped when its owner casts none of its
|
||
> roles.** *That is countable, and it is the same measurement that separates
|
||
> `annotating` from `cart` from `loom`.*
|
||
|
||
**So the sequence is: implemented → extracted → owned by someone who does not
|
||
implement it.** *Get the rehoming operation first (entry 47) and see whether the
|
||
resulting state still wants a name. **If it does, we will know because we keep
|
||
needing to say it** — which is a better reason to coin a word than noticing a
|
||
gap.*
|
||
|
||
---
|
||
|
||
## 49. A repository per thing that is built; published documents live with their publisher
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**`cart` and `annotating` do not get repositories of their own.** They are
|
||
published documents and they live with their publisher.
|
||
|
||
**Advances** not spending a repository on something with nothing manifest in it,
|
||
and closing a question I asked two days ago and never got an answer to.
|
||
|
||
**Because** loom's test is the right one: ***nothing will really be manifest in
|
||
them.*** A repository is for a thing that is built; **a published agreement is a
|
||
document, and a publisher may publish several.**
|
||
|
||
*This retires entry 26's implication that each agreement needed its own home.
|
||
**The requirement was that `origin:` point at something real, and one repository
|
||
satisfies it for both** — which is also why the Go-style path ends at the
|
||
repository and continues into the affordance, rather than assuming one repository
|
||
per published thing.*
|
||
|
||
---
|
||
|
||
## 50. `bedrock` — the seed repository is shaped like a registry export
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the repository formerly called `loom` is **`bedrock`**, and its structure is
|
||
**what a registry export would look like**, so that a registry can ingest it as
|
||
its starting point.
|
||
|
||
**Advances** the last of the BDUF problem, by replacing the thing we could not
|
||
write with the thing we can.
|
||
|
||
**Because** loom asked what if it were shaped as an export of a registry that
|
||
does not yet exist — and it inverts the failure from two entries ago exactly.
|
||
**A specification for an unbuilt system is reasoning. An export is data**, and
|
||
data can be wrong in ways somebody can see.
|
||
|
||
> **The registry either ingests it or it does not. That is a test, where a
|
||
> specification is an argument.**
|
||
|
||
### The consequence that makes this worth more than the naming
|
||
|
||
**It puts entry 40's invariant on the normal path.** The registry must be
|
||
rebuildable from the repositories that published to it — **and here that
|
||
reconstruction is not the disaster-recovery route, it is the bootstrap route.**
|
||
|
||
***Restore paths only used in disasters do not work in disasters.*** *This one is
|
||
exercised the first time anything runs, and every time afterwards. We did not
|
||
plan that and it is the strongest reason to do it.*
|
||
|
||
### Genesis is a distinct operation
|
||
|
||
**bedrock's identities are accepted as given on a first import into an empty
|
||
store.** Everything after is ordinary publication, which mints.
|
||
|
||
*Re-importing over a populated store mints fresh identities and breaks every pin
|
||
— **the same hazard as re-publishing during a rebuild**, and it earns the same
|
||
separation. **Genesis and publish must not share a code path.***
|
||
|
||
**On the URN-shaped identities, and signatures wanting to sneak back in:** loom's
|
||
instinct that they were creeping is correct, and **the answer is the one already
|
||
settled in entry 41.** A signature vouches for an identity **across a boundary of
|
||
trust**; there is no such boundary here — one tailnet, one operator — and **the
|
||
vouching is that the operator ran the import.** *Trigger unchanged: when someone
|
||
must verify without being able to ask.*
|
||
|
||
### And it keeps existing afterwards
|
||
|
||
**bedrock does not dissolve into the registry.** Entry 40 forbids it: a registry
|
||
must not hold content no repository holds. **But its role changes** — *it stops
|
||
being a fixture and becomes a repository that publishes, and changes reach the
|
||
registry by being published rather than imported again.*
|
||
|
||
*On the rename itself: cheap, because nothing is built on the old name, and
|
||
`loom/bedrock` resolves the awkwardness loom already flagged in `loom/loom`.
|
||
**`loom` is the project; `bedrock` is a repository in it** — and this is naming a
|
||
directory, not improving a term.*
|
||
|
||
*Consolidation performed at the same time, per entry 49: **`cart` and
|
||
`annotating` are inside bedrock**, since they are published documents and a
|
||
publisher may publish several.*
|
||
|
||
---
|
||
|
||
## 51. Reachability and ownership are independent, and conflating them is what makes multiple registries feel hard
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**many registries share one bedrock.** A consumer in one may pin a URN minted by
|
||
another. **An affordance's registry never changes; its owner may.**
|
||
|
||
**Advances** the enterprise shape being a consequence of what we already built
|
||
rather than a thing needing new machinery.
|
||
|
||
**Because** loom described a real topology — a platform per business capability,
|
||
quoting and claims each with a registry, all sharing bedrock — and asked whether
|
||
affordances are transportable across registries. **They are, and "transportable"
|
||
turns out to be two different questions that we had been asking as one:**
|
||
|
||
| | question | answer |
|
||
|---|---|---|
|
||
| **reachability** | *claims wants to use quoting's thing* | **pin it.** A URN carries its registry, so it resolves — nothing moves |
|
||
| **ownership** | *who maintains this now?* | **rehome it.** Entry 47: the owner changes, the identity does not |
|
||
|
||
> **You do not move an affordance in order to use it.** *Cross-registry pinning
|
||
> is the answer to reachability, and rehoming is the answer to maintenance.*
|
||
|
||
**And a cross-platform pin makes platform coupling visible**, which is the thing
|
||
an enterprise most wants and least has.
|
||
|
||
### The one thing that is genuinely expensive, stated so nobody discovers it
|
||
|
||
**A URN's minting registry is part of its identity and cannot change.** *Like an
|
||
ISBN's issuing authority: the publisher may change hands, the number does not.*
|
||
|
||
**So moving an affordance to a different registry means a new identity, and every
|
||
consumer must re-pin.** *That is expensive and it should be — it is a real change,
|
||
not an administrative one.* **Rehoming across owners is free; rehoming across
|
||
registries is not**, and the asymmetry is the design working rather than a gap.
|
||
|
||
*And this removes the pressure it looks like it creates. **You never need to
|
||
guess at publication time which registry a thing might one day belong to**,
|
||
because reachability never required moving it.*
|
||
|
||
*Note for entry 24: **multiple registries is the concrete shape that makes search
|
||
necessary.** "Which registry do I even look in?" is exactly a question the gap
|
||
list cannot answer — the un-hold condition, arriving with a topology rather than
|
||
a count.*
|
||
|
||
---
|
||
|
||
## 52. The affordance/staging split holds at bedrock, and here is the test for whether it is doing work
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**yes.** bedrock owns affordances; implementations own stagings of them.
|
||
|
||
> **The test: could two implementations differ in their *roles*, or only in their
|
||
> *beads*?**
|
||
>
|
||
> **Roles differ → the split is doing work.** *Only beads differ → you have a
|
||
> protocol with exactly one staging*, which is still legal and still uses the
|
||
> same terms.
|
||
|
||
**Advances** having a way to tell when the indirection earns its keep, which the
|
||
first handoff named as the place it most suspected itself.
|
||
|
||
**Because** loom asked whether the split still makes sense this high up, and it
|
||
does — **but not everywhere, and the difference is checkable rather than
|
||
felt.**
|
||
|
||
**Where it does work:** *hand something over so others can depend on it*, once
|
||
the registry has actually implemented it. **A registry that mints identities and
|
||
a registry built on content-addressing have genuinely different roles** — one
|
||
casts *mints an identity*, the other casts *derives identity from content*, and
|
||
there is no bead that turns one into the other. **Two stagings, one affordance,
|
||
and a consumer picks.**
|
||
|
||
**Where it does not:** `annotating` casts 6 roles of 6 and there is no second way
|
||
to do it. **The protocol is the staging.** *That is the commitment case from
|
||
entry 1, and it is fine — catalogue versus commitment is about whether
|
||
alternatives exist, not about what kind of thing something is.*
|
||
|
||
*So bedrock will hold both: affordances with exactly one staging forever, and
|
||
affordances with several. **Nothing distinguishes them structurally and nothing
|
||
should** — the second becomes the first the moment somebody implements it
|
||
differently, and that is not a change of kind.*
|
||
|
||
*On loom's Latin: **publish is to make public**, and it is worth noticing the
|
||
design agrees. **Publication is about crossing a boundary, and the boundary can
|
||
be redrawn without the thing moving** — which is the whole of entry 51 in one
|
||
word's etymology.*
|
||
|
||
---
|
||
|
||
## 53. Space is cheap. Time is what costs. The identity machinery is deferred with a trigger.
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**v1 serves the read scenarios with paths and prose.** Minting, receipts,
|
||
immutability, genesis and the ledger are **designed and deferred**, and the
|
||
trigger to build them is stated below.
|
||
|
||
**Advances** not building the apparatus before anything needs it — which is the
|
||
sin of the tool loom is afraid of becoming.
|
||
|
||
**Because** loom named three scenarios and **every one of them is a read**:
|
||
|
||
- *What are my options for publishing to my local cluster?*
|
||
- *Does Hypertheory.API let another app list current students? If not, it is a
|
||
gap.*
|
||
- *What are all these `.loom` things — what is a cart? a venue?*
|
||
|
||
**Running each against the design, none of them needs the identity machinery on
|
||
first use:**
|
||
|
||
| | needs | does not need |
|
||
|---|---|---|
|
||
| cluster options | published affordances, stagings, who pins them | URNs, receipts, a ledger |
|
||
| does the API do X | the same, plus gaps | the same |
|
||
| what is a cart | **bedrock being a repository you can clone** | any of it |
|
||
|
||
> **The write-side machinery is for time, not for space.** *Space is paths and
|
||
> prose. **Time is what costs**, and time has not started yet.*
|
||
|
||
**We spent two days on minting, receipts, immutability, genesis, rehoming and
|
||
ledgers — and about ten minutes on what a person actually reads.** The
|
||
justification for all of it is real: **it is what makes the read side trustworthy
|
||
after things change.** *Nothing has changed yet.*
|
||
|
||
**Today's mechanism for "did it change" is a git diff**, which is what
|
||
`must-revalidate` has meant all along and which cost loom two minutes the one
|
||
time it mattered.
|
||
|
||
> **The trigger: when a consumer is burned by a change they did not see.** Not a
|
||
> date, not a count of repositories. **One person surprised once.**
|
||
|
||
*What this demotes: entries 13, 31, 32, 33, 34, 40, 47 and 50 remain decided and
|
||
move behind that trigger. **They are not retracted** — a deferral with a stated
|
||
condition is what §8 asks for, and re-deriving them later is the cost entry 12
|
||
says we keep paying.*
|
||
|
||
---
|
||
|
||
## 54. Is this WSDL? Five ways it is not, and one way it becomes it
|
||
|
||
**Finding** (2026-09-06, loom + claude-substrate — `cart: almanac`): loom asked
|
||
directly and the question deserves the direct version, since **the answer is
|
||
mostly no and the exception is the thing loom just noticed.**
|
||
|
||
**What made WSDL bad**, so we are measuring against something rather than a
|
||
feeling: *it described shape rather than meaning; it was generated so nobody read
|
||
it; it produced codegen and therefore coupling; it grew to describe everything
|
||
and so said nothing; and it existed for machines, so humans stopped
|
||
understanding the system.*
|
||
|
||
**Five ways this is not that:**
|
||
|
||
- **It records meaning, not shape.** *"There is only a notion of a current
|
||
course; VMs are destroyed at the end of a session"* — **an interface document
|
||
carries none of those three facts**, and they are the ones a developer needs.
|
||
- **Nothing is generated from it.** No clients, no stubs, no coupling. **A pin is
|
||
a fact, not a binding.**
|
||
- **Only the delta from ambient knowledge.** WSDL described everything including
|
||
the obvious; **this refuses to document Kafka.**
|
||
- **It has a place for not knowing.** *Looked-at-and-empty*, gaps, uncast roles.
|
||
**An interface description has no way to say "we checked and there is nothing
|
||
there."**
|
||
- **Being wrong out loud is the mechanism.** WSDL was authoritative by
|
||
construction; **here a wrong entry that provokes a correction is a success.**
|
||
|
||
**The one way it becomes WSDL, and it is the live risk:**
|
||
|
||
> **If the write-side apparatus arrives before anyone reads anything.**
|
||
|
||
*Machinery built ahead of use is exactly how a description format becomes
|
||
ceremony — and the correction is entry 53, which loom prompted by noticing the
|
||
imbalance before it had a name.*
|
||
|
||
*Second-order risk, worth naming once: **receipts are generated lock files**, and
|
||
lock files are the closest thing here to WSDL's shape. **They stay safe only while
|
||
nothing generates code from them.** If anyone ever builds a client from a
|
||
receipt, this became the thing we were avoiding.*
|
||
|
||
---
|
||
|
||
## 55. A staging lives in the repository that implements it, not in a registry
|
||
|
||
**Correction** (2026-09-06, claude-substrate — `cart: almanac`): entry 43 said
|
||
*"each registry owns its staging"* and entry 52 asked whether *"two
|
||
implementations"* differ in their roles. **Both readings put implementations
|
||
inside registries, and nothing does.**
|
||
|
||
> **A repository implements. A staging lives with the implementation. A registry
|
||
> serves published descriptions of it, and differences from the shared thing are
|
||
> recorded in a venue file.**
|
||
|
||
**Nothing in the design was wrong; the phrasing was**, and it produced exactly
|
||
the confusion it should have. *Entry 52's test is unchanged and still holds — it
|
||
is a test about two **repositories**, not two registries.*
|
||
|
||
---
|
||
|
||
## 56. An affordance is a description; a staging is a table; a bead is a document
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
a staging is **one markdown file containing a table** — roles as rows, beads as
|
||
cells. Not a directory of role files.
|
||
|
||
```
|
||
affordances/<name>/affordance.md description, and links to its stagings
|
||
affordances/<name>/<staging>.md a table
|
||
beads/<name>.md a document
|
||
```
|
||
|
||
**Advances** the thing being stored in the shape it already has, and the read
|
||
scenarios from entry 53 being served by a file somebody can actually read.
|
||
|
||
**Because** loom asked whether an affordance could be *a named markdown file, a
|
||
description, and a table of roles and beads* — and **a table is what a staging
|
||
already is.** *A list of roles that must be cast.* **We had been storing a table
|
||
as a directory:** thirteen files for the cart affordance, where two and a
|
||
handful of beads say the same thing.
|
||
|
||
### It repairs the bootstrap's worst finding rather than routing around it
|
||
|
||
That finding was **role and bead are indistinguishable — every role file is a
|
||
name followed by a nominalisation of its own bead.** *They were indistinguishable
|
||
because they were the same file.*
|
||
|
||
> **A role is a label. A bead is a document. The table is the staging.**
|
||
|
||
**A row has nowhere to hide prose**, so the prose goes where it belongs, and the
|
||
distinction becomes structural instead of aspirational.
|
||
|
||
### Three things improved that were not aimed at
|
||
|
||
**Uncast roles are visible at a glance.** *Eight cast, four yours* is a column you
|
||
scan. **The measurement separating a protocol from a specification stopped being a
|
||
count and became a look.**
|
||
|
||
**The comparison view falls out.** Two stagings are two tables with the same row
|
||
labels — **which is exactly the tailnet-versus-public table in TAILSCALE.md**, the
|
||
thing identified on day one as the most valuable content in that document.
|
||
|
||
**The receipt is the same document with a different column.** Entry 34 called a
|
||
receipt *the staging with every role carried by an identity* — **so publishing
|
||
renders a column and restructures nothing.** *Names now, identities later, one
|
||
table throughout — which also means the deferral in entry 53 costs no rework.*
|
||
|
||
### And it dissolves the bootstrap's third finding
|
||
|
||
`affordances/<slug>/staging/` **could hold exactly one staging** and could not
|
||
express a catalogue. **A file per staging can**, and it is also required by entry
|
||
13: *a staging is addressed and `410`s, so it must be its own thing.*
|
||
|
||
*Cross-affordance dependencies become a link in a cell — cart's* **holds the
|
||
durable record** *points at the event-log affordance from inside the table, which
|
||
is more legible than the prose note it replaces.*
|
||
|
||
---
|
||
|
||
## 57. A consumer's file is the complement of the staging, not a copy of it
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
a consuming repository holds **a table containing only the rows the publisher
|
||
left uncast.** Nothing the publisher cast appears in it.
|
||
|
||
```
|
||
venues/<publisher host>/<org>/<repo>/<staging>.md
|
||
```
|
||
|
||
**Advances** the completeness of an adoption becoming checkable, which is a
|
||
freshness case we had already found and had no mechanism for.
|
||
|
||
**Because** loom asked whether a consumer's *cast with* column would just be the
|
||
bead URNs — **and running it through our own rules gives something better than a
|
||
copy.** *Say it once, where it is owned:* the eight cast roles belong to bedrock,
|
||
so reproducing them here is duplication. *Only the delta from ambient knowledge:*
|
||
the delta **is** the four casts. **Entry 30 already said this and this is its
|
||
shape.**
|
||
|
||
> **Their table has holes. Ours fills them. Put them side by side and you have
|
||
> the whole thing, with nothing said twice.**
|
||
|
||
### And that makes an open freshness case mechanical
|
||
|
||
We recorded that **a new *yours to cast* role appearing upstream leaves an
|
||
adoption incomplete, and neither `200` nor `410` says so** — because completeness
|
||
is not identity.
|
||
|
||
**With two tables it is a set difference.** *A role marked yours-to-cast upstream
|
||
with no row here is an incomplete adoption*, and it is one comparison rather than
|
||
a reading.
|
||
|
||
*Role labels are the join key, which is entry 3's carve-out working across a
|
||
publisher/consumer boundary rather than within a catalogue: **the publisher
|
||
imposes the vocabulary, so the labels collide for free.** A retyped label shows up
|
||
as **one unmatched row on each side**, which is a visible signature and needs no
|
||
new machinery.*
|
||
|
||
### What actually fills the column
|
||
|
||
**Not URNs, mostly.** A consumer's casts are *its own* beads — a presence, a path,
|
||
a branch — **and most of them are never published**, because nobody else depends
|
||
on them.
|
||
|
||
> **The column holds a link to a bead. Whether that bead has a URN depends on
|
||
> whether it was published, and a consumer's casts usually are not.**
|
||
|
||
*Which is `affordances/` versus `published/` showing up on the consumer side, and
|
||
it is the asymmetry that makes bedrock bedrock: **its beads exist to be depended
|
||
on; a consumer's exist to be used.***
|
||
|
||
*Small finding: **you adopt a staging, not an affordance** — a specific way of
|
||
doing the thing, not the thing. So the consumer's file is named for the staging,
|
||
which is also why the path can stop at the repository and does not need the
|
||
affordance slug.*
|
||
|
||
---
|
||
|
||
## 58. A derived rendering is a third category, and provenance is not the field we removed
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
a consuming repository may hold **a readable rendering of an adopted staging —
|
||
theirs and ours together — which is derived, carries what it was derived from,
|
||
and is never authored.**
|
||
|
||
**Advances** entry 53's third scenario — *what is a cart?* — being answerable
|
||
**with no network**, which the complement table alone does not do.
|
||
|
||
**Because** loom asked for the affordance to be readable as a markdown page, the
|
||
way a README projects `published/`. **The complement table is correct and it is
|
||
not legible**: four rows of our casts tell a stranger nothing about what a cart
|
||
*is*.
|
||
|
||
**And a derived rendering is not the duplication we forbade.** *Say it once,
|
||
where it is owned* prohibits **two authored copies that can drift silently.** A
|
||
rendering has one author — the derivation — and **says what it came from**, so
|
||
drift is detectable rather than silent.
|
||
|
||
> **Copy, reference, rendering. The first is forbidden, the second needs the
|
||
> network, and the third is what makes an offline reader possible.**
|
||
|
||
*And provenance is not `checked:` returning. **`checked:` recorded when we last
|
||
verified something we could ask about; provenance records what a derived artifact
|
||
was derived from.** The first is a note about diligence and became pointless when
|
||
asking got cheap. **The second is required by any derived artifact** — you cannot
|
||
tell a rendering is behind without knowing its source.*
|
||
|
||
---
|
||
|
||
## 59. Uncast roles borrow the annotating vocabulary, but must not borrow its file
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**an uncast role may be a prompt** — `Select:` with castable options, `Question:`,
|
||
`Affirm:` — and a consumer's answer may be `Skipped.` **The answers live in the
|
||
consumer's own file, never in a notes file.**
|
||
|
||
**Advances** a distinction the complement table could not make, using a
|
||
vocabulary both parties already read.
|
||
|
||
**Because** loom noticed the fit and it is close: **`cart.md`'s "Roles you must
|
||
cast" is already prompt-shaped**, and this repository answered it with `Select:`
|
||
and `Question:` headings weeks ago.
|
||
|
||
**What the vocabulary buys, and it is a real gap being closed:**
|
||
|
||
> **`Skipped.` — considered and passed — is not the same fact as an uncast role,
|
||
> and the complement table could not tell them apart.**
|
||
|
||
*Entry 22 gave four meanings for a role with no bead on the publisher's side.
|
||
**This is the fifth, and it is the consumer's**: we looked at what you left for
|
||
us, and chose not to fill it. **An unfilled role and a declined one must not read
|
||
alike**, and until now they did.*
|
||
|
||
**And `Select:` lets a publisher offer castings** — *"where does a round live?
|
||
`.loom/cart/`, elsewhere under `.loom/`, outside it entirely"* — **with
|
||
`annotating`'s rule attached: never enumerate an escape hatch.** *An option on
|
||
the list can be chosen and you are done; an answer that is not on the list has to
|
||
be written, so it arrives with a reason.*
|
||
|
||
### What must not be borrowed, and it is the half that would break
|
||
|
||
**Not the notes file.** *Casting by writing `cart.notes.md` beside an adopted
|
||
staging recreates the deadlock entry 30 documented:* **notes freeze their source,
|
||
the response is to rewrite the source, and the source is not ours to rewrite.**
|
||
|
||
**We are living inside that failure right now** — `cart.md` and `annotating.md`
|
||
have been frozen since 2026-08-21 for exactly this reason.
|
||
|
||
> **Take the prompts and the answer conventions. Leave the file.** *The
|
||
> consumer's casts belong in a file the consumer owns.*
|
||
|
||
---
|
||
|
||
## 60. A projection erases the machinery, and the diff of two projections is the change report
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
a projection of a staging is **prose with no vocabulary in it.** A heading, the
|
||
description, then a section per role carrying the body of its bead. **No links,
|
||
no tables, no mention of beads, roles, stagings or affordances.**
|
||
|
||
**And it must be pure: the same source renders the same bytes**, so that
|
||
re-fetching and diffing is the whole change-detection mechanism.
|
||
|
||
**Advances** the read scenarios costing what they are worth, instead of costing a
|
||
vocabulary lesson first.
|
||
|
||
**Because** I built the wrong thing and loom corrected it: my rendering had a
|
||
table, links and a *whose* column — **the ontology leaking into the output.**
|
||
loom's point is exact: ***in 99% of the places I'd use cart, I don't want to waste
|
||
context on "WTF are beads? Stagings?"***
|
||
|
||
> **The ontology is an authoring tool, not a reading format.** *It is how the
|
||
> document is assembled. It should not survive assembly.*
|
||
|
||
**And purity is a real constraint, which I had already broken.** My first
|
||
rendering carried *"derived … @ 2026-09-06"* — **a timestamp, so every re-render
|
||
diffs even when nothing changed.** *Provenance must be content-derived: a source
|
||
hash is pure, a date is not.*
|
||
|
||
### The consequence, which is larger than the formatting
|
||
|
||
**Re-GET and diff is the change report.**
|
||
|
||
*You do not need identity, `410`s or a ledger to know that something changed —
|
||
you need them to know **what kind** of change it was.* **For most uses, "the text
|
||
changed, and here is which paragraph" is the whole answer**, and it costs a `GET`
|
||
and a `diff`.
|
||
|
||
**That strengthens entry 53's deferral considerably.** The machinery was already
|
||
behind a trigger; **this shows the read side does not merely survive without it,
|
||
it has a good answer without it.**
|
||
|
||
### And several projections come off one source
|
||
|
||
**Prose** — *what is this and how do I use it.* **Comparison** — *this staging
|
||
against that one*, which is the table shape. **Precedent** — *who else casts
|
||
this.* **Each drops what the others need**, which is entry 5's *a projection is
|
||
audience-shaped* arriving with three concrete audiences instead of one abstract
|
||
claim.
|
||
|
||
---
|
||
|
||
## 61. The round trip: `cart.md` was always this projection, written by hand
|
||
|
||
**Finding** (2026-09-06, claude-substrate — `cart: almanac`): the prose
|
||
projection of the cart staging **reconstructs something very close to `cart.md`
|
||
itself** — the agreement's own sections map almost one-to-one onto the roles.
|
||
|
||
**Which makes the round trip a test we can actually run:** *decompose a document
|
||
into roles and beads, project it back, and compare.* **Where they differ is what
|
||
the decomposition lost**, and that is a better check on the ontology than any
|
||
argument about it.
|
||
|
||
**What it lost, and it is one section:** `cart.md` ends with **"What we know is
|
||
unsettled"** — *whether size is the only conversion trigger; whether specimen and
|
||
polad stay two roles.*
|
||
|
||
> **A published thing's own open questions have no home.**
|
||
|
||
**Not a role** — nothing casts them. **Not a gap** — nothing is needed from
|
||
anyone. **Not a decline** — nothing was refused. **Not a write-ahead entry** —
|
||
that is the author's private workspace, and these were published deliberately, to
|
||
be argued with.
|
||
|
||
*This is the fourth or fifth time this cart has found a place for not-knowing and
|
||
been right to. **This one is the publisher's own uncertainty, published on
|
||
purpose**, and I do not have a home for it. Recorded rather than solved.*
|
||
|
||
---
|
||
|
||
## 62. Casting by annotation, and the freeze is the feature
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**a consumer may cast a staging's open roles by annotating the projection.** The
|
||
resulting freeze is **deliberate**, and an upstream change arrives as **a polad**.
|
||
|
||
**Advances** a fetch that would overwrite becoming a decision somebody makes,
|
||
rather than a file that changes underneath them.
|
||
|
||
**Because** loom proposed it and **it dissolves the objection I raised one round
|
||
earlier.** I said notes would freeze a source we cannot rewrite — *and that was
|
||
true of `cart.md`, which was adopted by copy from somebody else.* **The
|
||
projection is ours**: derived into our own tree, and regenerable. **The deadlock
|
||
does not reach it.**
|
||
|
||
### The freeze is the point, not the cost
|
||
|
||
**Once notes exist the projection is fixed**, so **a re-fetch cannot silently
|
||
overwrite the text the casts are anchored to.**
|
||
|
||
> **Without the freeze, `fetch` clobbers. With it, `fetch` has to become a
|
||
> decision.**
|
||
|
||
*That is entry 11's failure — a divergence nobody announces is indistinguishable
|
||
from a plan that was followed — and the freeze is what makes this one announce
|
||
itself.*
|
||
|
||
**And the new projection is a polad by definition rather than by analogy:** *a
|
||
candidate artifact, shaped exactly as it would be if kept.* **Two exits.**
|
||
Applying means dissolving — carry the casts into the record, delete the notes,
|
||
re-render, and the projection is released.
|
||
|
||
### Where the casts live, which is the part that had to be got right
|
||
|
||
**A projection is derived from *their staging* plus *our casts*, and is pure
|
||
given both.** So the casts **cannot live only in the notes**: the notes die at
|
||
dissolve, and purity breaks if a projection carries authored content nothing else
|
||
holds.
|
||
|
||
> **Annotations are how a cast is made. The complement table is where it is
|
||
> kept.**
|
||
|
||
*Which is the two-stage shape this project keeps arriving at without designing
|
||
it: **cart → event log**, **write-ahead log → event log**, **cart gaps → durable
|
||
gaps**, and now **notes → complement table.** **Four instances.** At this point it
|
||
is a property of the thing rather than a coincidence.*
|
||
|
||
*Proportionality, stated so this does not become ceremony: **available, not
|
||
mandatory.** If you know your casts, write the table and be done. **Annotate when
|
||
there is something to work out** — which is the same test the cart itself uses.*
|
||
|
||
---
|
||
|
||
## 63. Content negotiation is required, not convenient — and the ETag finishes the freshness story
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**one address per staging, several representations.** `Accept` selects between
|
||
the browsing surface and the prose projection. **The server returns an `ETag`,
|
||
and `If-None-Match` is the freshness check.**
|
||
|
||
**Advances** the read side's change detection being entirely stock HTTP, which is
|
||
what entry 53's deferral was betting on.
|
||
|
||
**Because** loom asked whether a projection could come back from the same place —
|
||
**and it must, rather than merely may.**
|
||
|
||
> **Two URLs would be two identities for one staging.** *And then which one
|
||
> `410`s?* **Identity is the thing this design has been most careful about, and
|
||
> separate addresses for representations would spend it for nothing.**
|
||
|
||
*It is also `410`/`303` again: lean on what the web already does, because an
|
||
agent knows `Accept` without being taught — entry 9, at zero bytes.*
|
||
|
||
### The three status codes cover the whole read-side freshness story
|
||
|
||
| | meaning | cost |
|
||
|---|---|---|
|
||
| **`304`** | nothing changed | **no body** |
|
||
| **`200`** | changed — here it is, diff it | one fetch |
|
||
| **`410`** | replaced — go to the affordance | one fetch, then a `303` |
|
||
|
||
**No custom machinery anywhere in that table.** *loom's "basically a hash of what
|
||
is at the server" is an `ETag`, and every HTTP client already implements the rest
|
||
of it.*
|
||
|
||
**Provenance carries the source `ETag`**, so *is my projection behind?* is a
|
||
**`HEAD` with `If-None-Match`** — no body transferred, which is about as cheap as
|
||
freshness gets.
|
||
|
||
### The catch, which would otherwise be found painfully
|
||
|
||
**A projection is derived from *their staging* plus *our casts*. The server can
|
||
only hash its half.**
|
||
|
||
> **The server's `ETag` answers *has upstream changed*. It cannot answer *is my
|
||
> rendered file current*, because it has never seen our casts.**
|
||
|
||
**Two hashes, two questions:** the source `ETag` in the provenance comment, and —
|
||
if we want it — a local hash over *source `ETag` + our casts*. *Conflating them
|
||
would report a projection as current after we had edited our own half.*
|
||
|
||
*And `ETag`s are per-representation: the browsing surface and the prose
|
||
projection have different bytes and therefore different tags. **`Vary: Accept`**,
|
||
or a cache serves one where the other was asked for.*
|
||
|
||
---
|
||
|
||
## 64. Confidence in the representation, stated as a split rather than a number
|
||
|
||
**Finding** (2026-09-06, claude-substrate — `cart: almanac`): loom asked how
|
||
confident I am that an affordance can be represented meaningfully. **The honest
|
||
answer has three parts and only one of them is high.**
|
||
|
||
**High — agreements and protocols.** *Three done on real content: `cart`,
|
||
`annotating`, `record a decision`.* **None had to be forced**, the table shape
|
||
improved legibility rather than costing it, and **entry 61's round trip
|
||
reconstructs something close to the source document** — which is a real check on
|
||
faithfulness rather than an argument about it.
|
||
|
||
**Untested — code and running services.** *Not one of the three is code, a
|
||
cluster, or an API.* **The Hypertheory.API case has never been attempted**, and it
|
||
is the one loom actually needs.
|
||
|
||
**Known chafe — the gotchas.** *`ingressClassName` is not optional; CNPG cron has
|
||
six fields.* **Three times now these have had nowhere to live** — not roles, not
|
||
beads, not stagings — and the best home found is a note on a role, which works
|
||
and still feels wrong.
|
||
|
||
### And the caveat that matters more than the count
|
||
|
||
**The three successes are correlated.** *Same author, same kind of artifact, and
|
||
all three were already well-organised documents with numbered sections that map
|
||
onto roles.*
|
||
|
||
> **We may have demonstrated that a well-structured document decomposes, rather
|
||
> than that the ontology is right.** *And everything real is badly structured.*
|
||
|
||
**The test that would move this:** *project something nobody wrote with this in
|
||
mind.* **The cluster was half of that** — projected from documents I had read but
|
||
did not write — **and it produced the gotchas problem immediately**, which is
|
||
exactly the signal to expect.
|
||
|
||
*So: high confidence for the case we have done repeatedly, and **the number I
|
||
would not give is one for the case loom most needs.***
|
||
|
||
---
|
||
|
||
## 65. A new or updated external starts life in the cart as a polad
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**nothing is written directly into `venues/`.** A candidate external — new or
|
||
updated — **lands in the cart as a polad**, and *applying* is the move out.
|
||
|
||
**Advances** taking on a dependency being an act somebody performs rather than a
|
||
file that appears.
|
||
|
||
**Because** loom proposed it, and **the strongest reason is one I would not have
|
||
given: the cart has two parties.**
|
||
|
||
> **An external is a claim about somebody else's thing, and the other presence in
|
||
> the round may know it is wrong.**
|
||
|
||
*loom's own example from days ago: **"ohhh — that's a leftover from an old way of
|
||
thinking."*** **A polad gives them the chance to say it.** *Written straight into
|
||
`venues/`, an external is a claim nobody reviewed — and entry 8 says being wrong
|
||
out loud is the mechanism, which requires somebody to be there when you are
|
||
wrong.*
|
||
|
||
**And it makes one path where there were two.** Entry 62 already routed *updates*
|
||
through a polad. **A new adoption bypassing that would be the same destination
|
||
with no review step**, which is how the reviewed path becomes the optional one.
|
||
|
||
**It is also a polad by definition rather than analogy** — *a candidate artifact,
|
||
shaped exactly as it would be if kept* — **and applying is a `mv`.**
|
||
|
||
### Two cases, one path
|
||
|
||
**First adoption:** everything starts in the polad, is cast there, and applies
|
||
out.
|
||
|
||
**Update:** the candidate sits in the polad **while the current one stays live**.
|
||
*You do not take the live projection down in order to look at its replacement*,
|
||
and applying replaces it.
|
||
|
||
*Which makes `polad/venues/` an inbox: **everything in it is something we are
|
||
about to depend on and have not yet committed to.** That is a useful listing even
|
||
working alone.*
|
||
|
||
### And the case where there is no open round needs no new rule
|
||
|
||
**No `current/` means no cart.** So an external arriving outside a round has
|
||
nowhere to land — *which reads like friction and is the existing test in
|
||
disguise.*
|
||
|
||
> **If you know, write it. If you are unsure, that is a WTF, and the correct
|
||
> answer to any WTF is create a cart.**
|
||
|
||
*Adopting an external you are confident about is not a conversation. Adopting one
|
||
you are not **is**, and the rule that already covers it is the one loom wrote.*
|
||
|
||
---
|
||
|
||
## 66. The dyad assumption is inherited, not introduced — and here is what degrades
|
||
|
||
**Finding** (2026-09-06, claude-substrate — `cart: almanac`): loom noted that
|
||
entry 65 assumes two parties working together, and that this will not always
|
||
hold. **True, and worth separating from where it came from.**
|
||
|
||
**The assumption is `cart.md`'s, not entry 65's.** *One daily per presence; there
|
||
is never a third; when one party cannot feel a cost the decision belongs to the
|
||
party who can.* **And `cart.md` already names the hole it leaves:** *"if neither
|
||
presence can feel it — two agents with no human — size is not an available
|
||
trigger, and we do not have a good answer for that case."*
|
||
|
||
**So entry 65 inherits a known limitation rather than adding one.** *Which is the
|
||
useful thing to know: fixing it is upstream work on the agreement, not a patch
|
||
here.*
|
||
|
||
### What survives a single party, and what does not
|
||
|
||
**Lost: the review.** *An external written straight into `venues/` is a claim
|
||
nobody checked* — and entry 8's *being wrong out loud* needs somebody there when
|
||
you are wrong. **Alone, that reason evaporates.**
|
||
|
||
**Kept, and these are not small:**
|
||
|
||
- **The inbox.** `polad/venues/` lists everything you are about to depend on and
|
||
have not committed to. *Useful with nobody else in the room.*
|
||
- **The act.** Applying is a `mv`, so taking on a dependency stays something
|
||
performed rather than a file that appeared.
|
||
- **Continuity across sessions**, which is the one I would not have listed and is
|
||
probably the strongest here. **The agent in this dyad is discontinuous** — a
|
||
fresh instance has none of the conversation. *A polad is a surface a later
|
||
instance can read: **here is what was about to be committed to, and why it had
|
||
not been.** The conversation does not survive the round; the polad does.*
|
||
|
||
> **So the rule is worth keeping solo, for weaker and different reasons — and
|
||
> those reasons should be written down, or somebody alone will reasonably
|
||
> conclude the ceremony is for nothing.**
|
||
|
||
*And on the specific case loom raised: **the current arrangement is not solo.**
|
||
loom is a person and an agent; claude-substrate is another. The genuinely
|
||
uncovered case is a human with no agent, or agents with no human — **and the
|
||
second is the one `cart.md` says it cannot handle.**
|
||
|
||
---
|
||
|
||
## 67. `event-log.md` moves to the root of `.loom/`, and the legacy path is not inherited
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**there is no `internal/` directory.** The event log sits at `.loom/event-log.md`.
|
||
|
||
**Advances** a word not meaning two things in one document, and a known-bad path
|
||
not being carried into repositories that have no reason to have it.
|
||
|
||
**Because** loom caught a collision I had written myself: **the structure
|
||
document said *"there is no `internal/` beside `published/`"* in one paragraph and
|
||
described `internal/` two paragraphs later.** *Same word, two meanings — one a
|
||
refused category, the other an actual directory — which is exactly how a word
|
||
becomes shadow policy.*
|
||
|
||
**And the directory was wrong on its own merits, separately from the collision:**
|
||
|
||
- **It held one file.** *A directory holding one file is usually a directory that
|
||
should not exist.*
|
||
- **It named an audience distinction that does not apply.** *Nothing here is
|
||
hidden from anybody; `internal` describes a disposition, and a false one.*
|
||
- **The original handoff already said so:** *"that path is not where we would have
|
||
put it. A tool has it hardcoded, from before we understood §4… Consider it a
|
||
live demonstration — the price of an emitted path is real, nameable, and gets
|
||
paid by someone who wasn't there."*
|
||
|
||
> **The point of that note was to stop the cost being paid forward. Copying the
|
||
> path into every new repository would have been paying it forward.**
|
||
|
||
*So: **this repository keeps `.loom/internal/event-log.md`**, because a tool reads
|
||
it and breaking that today buys nothing. **`bedrock`, `registry` and everything
|
||
after use `.loom/event-log.md`.** The mistake stops here rather than
|
||
propagating.*
|
||
|
||
### And a rule fell out that was worth more than the fix
|
||
|
||
> **A stream is a file. A collection of things with identity is a directory.**
|
||
|
||
*The event log is appended to and no entry is ever addressed; a bead is one of
|
||
many and each is referred to. **`gaps/` stays a directory** because a gap is
|
||
reported, homed or withdrawn individually — it has identity, where a log entry
|
||
has only a position.*
|
||
|
||
---
|
||
|
||
## 68. `external` and `venues` have different units, which is the weak spot — one file per thing
|
||
|
||
**Decided** (2026-09-06, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**the unit is the thing.** `external/` and `venues/` merge into **one directory,
|
||
one file per thing we do not own**, with what-we-understand and what-we-decided
|
||
as sections rather than as separate trees.
|
||
|
||
**Advances** a reader not having to learn a distinction before they can find
|
||
anything.
|
||
|
||
**Because** loom asked what the unit of an external is, and **tracing it exposes
|
||
that the two directories never had the same one:**
|
||
|
||
| | unit | example |
|
||
|---|---|---|
|
||
| `external/` | **the thing** | *"there is only a notion of a current course; VMs are destroyed at the end of a session"* |
|
||
| `venues/` | **the staging** (entry 57) | *the four roles we cast on `cart`* |
|
||
|
||
**The content settles it.** *VMs are destroyed at the end of a session* is not a
|
||
fact about one affordance — **it is a fact about the whole thing**, and it is
|
||
exactly the sort of sentence that makes an external worth having. **So the unit
|
||
is the thing, and `venues/` having a different one is the inconsistency loom
|
||
could feel and I had not named.**
|
||
|
||
### The deeper problem, which is worse than the mismatch
|
||
|
||
**One fact splits across two files.**
|
||
|
||
- *"`traefik` is the default IngressClass, so omitting it publishes publicly"* —
|
||
**understanding.**
|
||
- *"we always set `ingressClassName` explicitly"* — **a decision.**
|
||
|
||
**Two sentences about one thing, in two trees, and a reader needs both.** *The
|
||
existence conditions from entry 6 are genuinely different — a file exists when
|
||
the reader cannot be assumed to know the thing, or when we decided something —
|
||
but **different existence conditions do not require different directories.** A
|
||
file may exist for either reason and carry both.*
|
||
|
||
### What the split was protecting, and how it survives a merge
|
||
|
||
**The two go stale from different directions**, which is the one real thing the
|
||
separation encoded:
|
||
|
||
> **Understanding goes stale when *they* change. A decision goes stale when *we*
|
||
> change our minds.**
|
||
|
||
**A heading carries that.** *It does not need a tree.*
|
||
|
||
### The shape
|
||
|
||
```
|
||
external/
|
||
├── kafka.md no address: what we decided about it
|
||
└── <host>/<org>/<repo>/
|
||
├── <repo>.md what we understand about the thing
|
||
├── <staging>.md what we cast, per adopted staging
|
||
└── <staging>.projection.md derived
|
||
```
|
||
|
||
**Things with an address are filed under it; things without one sit at the top by
|
||
name.** *Which also stops `venues/` carrying two shapes for two kinds of thing —
|
||
it was doing that because the things differ, not because we do.*
|
||
|
||
*Confidence, stated because loom named this as the weak spot and I do not want to
|
||
sound more certain than I am: **the diagnosis I believe** — different units, one
|
||
fact split in two. **The merge is the fix I would choose and it is a layout loom
|
||
has to live in**, so it is theirs to reject. *What would change my mind: a real
|
||
case where somebody wants to read every decision without reading any
|
||
understanding.* I could not construct one.*
|
||
|
||
---
|
||
|
||
## 69. Three problems, and the ontology is the third — bracket it
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
what is being built is **a documentation service whose goal is agent
|
||
orientation.** Affordances, stagings, roles and beads are **a later layer and are
|
||
bracketed.**
|
||
|
||
| | |
|
||
|---|---|
|
||
| **1** | pull descriptions of infrastructure and practice into a repository; keep them fresh; declare gaps against them |
|
||
| **2** | name a thing and get an overview, its role, and a link to its repository |
|
||
| **3** | affordances, stagings, roles, beads — **bracketed** |
|
||
|
||
**Advances** the two problems loom actually has being solvable now, instead of
|
||
waiting on a model built for a third.
|
||
|
||
**Because** loom's diagnosis is right and it is about mixing altitudes: **we were
|
||
fitting high-level infrastructure and practice description into a model built for
|
||
spans of source.** *A square peg into a bead-shaped hole.*
|
||
|
||
**And this is not a reversal.** *Entry 53 already deferred the write-side
|
||
machinery behind a trigger, for the same reason — the read scenarios did not need
|
||
it.* **This goes one step further and defers the model as well.** Same direction,
|
||
one more step.
|
||
|
||
### What relaxes, and each of these was a constraint of the third problem
|
||
|
||
**`external/` may hold a copy.** *Not a violation of refer-never-contain, because
|
||
that rule forbids **a copy that reads as authoritative and can drift with nothing
|
||
to notice.*** **A copy pinned to the publisher's `ETag`, with a conditional `GET`
|
||
to check it, cannot drift silently.** *The rule was never "do not cache."*
|
||
|
||
**No decomposition, so no projection.** *Entry 60's prose rendering existed to
|
||
reassemble readable text from decomposed parts.* **If you never decompose, you
|
||
already have their document.** *Entry 60 is not wrong; it is unneeded until
|
||
problem 3.*
|
||
|
||
**The mutation API may be ordinary resource-oriented REST.** *The prohibition
|
||
existed because a submitted document had to declare **replace this staging**
|
||
versus **edit this staging**, which produce different outcomes.* **With no
|
||
stagings there is nothing to distinguish**, and a description document is a
|
||
document. *It comes back when problem 3 does.*
|
||
|
||
**The delta rule moves upstream rather than weakening.** *Publishers still write
|
||
only what a reader cannot be assumed to know; **consumers cache what they wrote,
|
||
whole.***
|
||
|
||
### Three things I would check before building on it
|
||
|
||
**The lock must be the publisher's `ETag`, verbatim — never a hash we compute.**
|
||
*A fetch that normalises whitespace, or a proxy that re-encodes, breaks a locally
|
||
computed digest and reports a change that did not happen.*
|
||
|
||
**Unfreezing is by replacement, not by rewriting**, and this is the third time it
|
||
has come up. *The annotating protocol says dissolve by rewriting the source, and
|
||
the source is theirs.* **loom's answer is right: the new copy arrives as a polad,
|
||
the annotations are reconciled against it, and the pair is replaced wholesale.**
|
||
*Worth writing into the agreement, since it is the move `annotating` does not
|
||
have.*
|
||
|
||
**Problem 2 is a name lookup, not the search entry 24 holds.** *That hold was
|
||
about finding an affordance by what it does, across everything.* **Looking up
|
||
`hall-monitor` by name, among things you own, is a directory** — cheap, and it
|
||
needs none of the machinery the hold was protecting us from.
|
||
|
||
---
|
||
|
||
## 70. What bracketing problem 3 defers — recorded as a decline, not forgotten
|
||
|
||
**Decided** (2026-09-07, claude-substrate — `discovered`, `cart: almanac`):
|
||
the payoffs that require decomposition are **deferred with the model**, and named
|
||
here so that later they are recognised as deferred rather than rediscovered as
|
||
missing.
|
||
|
||
**Advances** the difference between *we chose not to* and *we never thought of
|
||
it*, which is the whole reason declines exist.
|
||
|
||
**What goes with problem 3:**
|
||
|
||
- ***How many ways do we verify credit cards?*** — needs stagings to compare, and
|
||
entry 3 already said comparison is authored at the affordance.
|
||
- ***If I change this, what breaks?*** — needs pins onto beads.
|
||
- ***Precedent as the unit of teaching*** — entry 20's *three applications already
|
||
do this*, which needs something to pin to.
|
||
- **Alternative stagings of one affordance**, and the catalogue/commitment split
|
||
that entry 1 built.
|
||
|
||
> **All three payoffs need a scale we do not have.** *One person, two clusters,
|
||
> five applications. **The credit-card question is an enterprise question and we
|
||
> are not an enterprise yet.***
|
||
|
||
*What survives into problems 1 and 2 unchanged, because it was never about the
|
||
model: **gaps**, **gap-first ordering**, **gaps kept in the reporting repository**,
|
||
**being wrong out loud**, **the asked-not-enforcing posture**, **a place for not
|
||
knowing**, **the cart and annotating agreements**, and **`ETag`-based freshness.***
|
||
|
||
*And loom's own last line is the test to keep: **a bead should refer, not
|
||
contain.** It is still true where beads are — **and where there are no beads, it
|
||
was never the question.***
|
||
|
||
---
|
||
|
||
## 71. The dissolve is inverted, and an annotation is an index from their text to our artifacts
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
when an adopted description changes, **the annotations mostly do not.** The
|
||
question is not *what do we rewrite here*, it is:
|
||
|
||
> **Given what changed in their document, what do we change in our code?**
|
||
|
||
**An annotation should therefore name the artifacts it justifies.**
|
||
|
||
**Advances** reconciliation having a direction, instead of being a rewrite that
|
||
cannot happen because the document is not ours.
|
||
|
||
**Because** loom put it exactly: *"we can't dissolve our annotations into their
|
||
document — so it is the same thing, sort of on its head."* **In the normal
|
||
protocol the notes are about a source and the response modifies that source.
|
||
Here the response modifies something else entirely** — the manifests, the config,
|
||
the code we built from it. *The annotations may survive unchanged apart from line
|
||
numbers.*
|
||
|
||
### Which makes an annotation more than commentary
|
||
|
||
> ***"We are using the tailscale local cluster approach here, and the manifests
|
||
> in our repo are based on that."***
|
||
|
||
**That is anchored to a span of their document and names something of ours. So it
|
||
answers, in the only direction that matters: *if this span changed, what of ours
|
||
should be looked at?***
|
||
|
||
**An annotation is an index from their text to our artifacts**, and that is what
|
||
makes reconciliation tractable rather than a re-read of everything.
|
||
|
||
**Authoring rule that falls out:** *"we use the tailnet approach"* is weak.
|
||
***"we use the tailnet approach — see `manifests/ingress.yaml`"*** is the whole
|
||
value. **Name what it justifies.** *Referring to our own paths from our own files
|
||
publishes nothing.*
|
||
|
||
### And it partially un-defers something entry 70 had just written off
|
||
|
||
**Entry 70 deferred *if I change this, what breaks?* on the grounds that it needs
|
||
pins onto beads.** *That is the **provider's** question and it is still
|
||
deferred.*
|
||
|
||
**The consumer's version survives the bracketing and costs nothing:** *their
|
||
document changed — which of our things did we say were based on it?* **The
|
||
annotation already answers it.**
|
||
|
||
*Coarse rather than mechanical — somebody reads and judges — and that is the
|
||
right resolution for one person with two clusters.*
|
||
|
||
---
|
||
|
||
## 72. The form is etcd-shaped: kinds and paths, not pages and links
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**the structure is the schema.** A small fixed set of kinds, each a directory,
|
||
enumerable at every level, with nothing living outside them. **No watch.**
|
||
|
||
**Advances** the thing having a form loom can name, rather than being defended as
|
||
*not a wiki* every time somebody asks.
|
||
|
||
**Because** loom reached for *etcd as wrapped by the Kubernetes API, but
|
||
browsable*, and the analogy is load-bearing rather than decorative. **What that
|
||
API gives is a total addressing scheme with enumeration at every level** — kind,
|
||
then namespace, then name — so you find things **by knowing the shape** rather
|
||
than by following links from somewhere.
|
||
|
||
> **A wiki's unit is a page and its structure is links. Here the unit is a kind
|
||
> and the structure is the path.**
|
||
|
||
**Type-by-location already is that.** *The directory is the kind; the path is the
|
||
address; `ls` is `LIST`.* **We had the mechanism and not the name for what it was
|
||
buying.**
|
||
|
||
### The kind-set, and why the bracketing is evidence for itself
|
||
|
||
**Under problem 3 there were eight kinds.** Bracketing it leaves **five**:
|
||
`external/`, `venues/`, `gaps/`, `cart/`, `event-log.md`.
|
||
|
||
> **A set small enough to hold in your head is what makes it a schema rather than
|
||
> a wiki.** *Halving it was the bracketing paying out, and I did not expect the
|
||
> payment to arrive as legibility.*
|
||
|
||
### No watch, and the reason is one we already gave
|
||
|
||
**Kubernetes `watch` is a client-initiated long poll** — pull-shaped, even though
|
||
it feels like push. **We still do not want it.**
|
||
|
||
*It would be the first thing that makes the registry hold persistent knowledge of
|
||
its consumers, which is the line drawn at pins-are-not-subscriptions.* **And a
|
||
conditional `GET` is sufficient for documents that change monthly.**
|
||
|
||
*Strictness is asymmetric and that is deliberate: **the write side owns what is
|
||
valid and accepts only known kinds; the read side shows what it does not
|
||
recognise rather than failing on it.** Files are edited outside the API
|
||
constantly, so the reader must be generous — but nothing arriving **through** the
|
||
API gets to invent a kind.*
|
||
|
||
---
|
||
|
||
## 73. Siblings, not sections — because one of the files is not ours
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
the sibling-file pattern generalises beyond notes.
|
||
|
||
```
|
||
externals/homelab-cluster.md theirs — a pulled copy, frozen, replaced whole
|
||
externals/homelab-cluster.usages.md ours — what we use, and what of ours depends on it
|
||
externals/homelab-cluster.gaps.md ours — what we expected here and did not find
|
||
```
|
||
|
||
**Advances** everything about one thing sitting together in a listing, while the
|
||
thing we do not own stays a single replaceable file.
|
||
|
||
**Because** loom has thought for a long time that the annotating pattern is
|
||
broader than the cart, and it is. **But the decisive reason is structural and it
|
||
arrived with yesterday's reframe:**
|
||
|
||
> **`homelab-cluster.md` is *theirs*. Everything beside it is *ours*.**
|
||
|
||
**Entry 68's merge is now impossible and I should say so plainly.** *I proposed
|
||
merging `external/` and `venues/` into one file with sections — and that was
|
||
written when an external was **our writing about their thing**.* **Entry 69 made
|
||
it a copy of their document**, and a merged file would put their content and ours
|
||
in one place, **which is the thing this entire round has been avoiding.** *The
|
||
reframe invalidated my fix a day after I wrote it.*
|
||
|
||
### Two facets, not three, and the test is what reconciliation asks
|
||
|
||
I nearly split deviations out as a third. **They answer the same question as
|
||
usages:**
|
||
|
||
| facet | on a new version, ask |
|
||
|---|---|
|
||
| **usages** | *did their change affect what we do — and which of our artifacts?* |
|
||
| **gaps** | *did their change fill this?* |
|
||
|
||
**A deviation is a usage that departs, and reconciliation treats them
|
||
identically.** *Facets are distinguished by the question reconciliation asks of
|
||
them, not by how they read.*
|
||
|
||
### And this dissolves the weak spot mechanically
|
||
|
||
loom named `external` versus `venues` as the weak spot and was right. **Under the
|
||
sibling pattern the line stops being conceptual and becomes checkable:**
|
||
|
||
> **`externals/` is for things you can fetch and re-fetch. `venues/` is for
|
||
> things you cannot.**
|
||
|
||
*Kafka, React and HTTP have no document to pull, no `ETag`, and nothing to
|
||
reconcile against — **so they have no stem to be a sibling of.*** **The
|
||
distinction is now "is there an upstream?" rather than "understanding or
|
||
decision", and nobody has to adjudicate a sentence.**
|
||
|
||
---
|
||
|
||
## 74. Gaps need reconciling too, and moving one *is* homing it
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**a gap is reconciled against a new version like everything else**, and a gap
|
||
beside an external is a **homed** gap.
|
||
|
||
**Advances** the good half of reconciliation — *a thing you needed now exists* —
|
||
being noticed rather than waited on.
|
||
|
||
**Because** loom asked whether gaps also need reconciling. **Yes, and it is the
|
||
half nobody builds for:** *the cluster publishes a new version that adds
|
||
publishing a container image, and our workaround has been unnecessary since.*
|
||
**Entry 24 said that when a gap is homed every workaround that should be retired
|
||
is findable — but assumed somebody tells us.** *Reconciling on fetch is how we
|
||
find out without being told.*
|
||
|
||
### The naming collision, and why it turns out not to be one
|
||
|
||
**`externals/<thing>.gaps.md` and `gaps/` are the same word in one tree**, which
|
||
is the `internal/` mistake loom caught yesterday — *except here the two meanings
|
||
are two positions of one thing.*
|
||
|
||
- **`gaps/`** — *unhomed.* **Addressed to nobody**, because you could not say
|
||
whose job it is.
|
||
- **`externals/<thing>.gaps.md`** — *homed.* **You can name whose job it is**, so
|
||
it is a claim against them and belongs beside them.
|
||
|
||
> **Moving a file from `gaps/` to `externals/<thing>.gaps.md` is the homing
|
||
> operation.** *Entry 40 said a gap stays unhomed until somebody claims it, at
|
||
> which point it resolves or moves into that external's file. **That `mv` is the
|
||
> claim.***
|
||
|
||
*Type-by-location carrying a state transition rather than a type — and it is the
|
||
one case where that is right, because **the position is the claim**, not a label
|
||
on it.*
|
||
|
||
---
|
||
|
||
## 75. An external is always somebody's document, never the technology
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**Kafka can be both**, and the apparent conflict comes from a slip in what the
|
||
external names.
|
||
|
||
> **You never fetch Kafka. You fetch your platform team's document about Kafka.**
|
||
|
||
`externals/acme.com/platform/kafka.md` is **a document with an owner and an
|
||
`ETag`.** *Apache publishes nothing into your registry; your platform team does,
|
||
and at a company that document is where the choices already made for you live —
|
||
auth, topic policy, what you may not turn on.*
|
||
|
||
**And `venues/kafka.md` still exists** for choices with **no upstream document to
|
||
hang them on.** *Both can be true of one technology, because they are two
|
||
different things: theirs and ours.*
|
||
|
||
**Advances** entry 73's line surviving the case that looked like a
|
||
counter-example.
|
||
|
||
**Because** loom is right that at a company much is decided for you, and **that
|
||
decision lives in a document somebody owns.** *It was never the technology that
|
||
was fetchable.*
|
||
|
||
*Operational tie-break, since the boundary is otherwise a judgement per sentence
|
||
and those decay: **put a choice where reconciliation will look for it.** If there
|
||
is a document it is about, it goes beside that document. **When in doubt, the
|
||
sibling** — a choice filed in `venues/` is a choice nobody will re-examine when
|
||
the upstream moves.*
|
||
|
||
---
|
||
|
||
## 76. The annotating "abstract class" is not one — it is roles nobody cast, and that is the deadlock
|
||
|
||
**Finding** (2026-09-07, loom + claude-substrate — `cart: almanac`): loom noticed
|
||
the annotating pattern appears in several forms and reached for *an abstract
|
||
class with subclasses.* **It is better than that, and it explains a bug we have
|
||
hit three times.**
|
||
|
||
**The three appearances, with what differs:**
|
||
|
||
| | source owned by | freezes when | dissolves by |
|
||
|---|---|---|---|
|
||
| **cart dailies** | us | notes exist | rewrite the source, delete notes |
|
||
| **an adopted agreement** | them | notes exist | ***nothing*** |
|
||
| **an external** | them | the `ETag` lock | replace whole, reconcile |
|
||
|
||
**Same roles throughout. Different beads.** *Which by entry 52's own test means
|
||
this is **one protocol with roles left to the adopter**, not an affordance with
|
||
alternative stagings.*
|
||
|
||
> **`annotating` publishes a staging in which *freezes the source* and
|
||
> *dissolves* are yours to cast — and it does not say so.**
|
||
|
||
### And that is why `cart.md` has been frozen since 2026-08-21
|
||
|
||
**The adopted-agreement case never cast *dissolves*.** *Not because anyone chose
|
||
badly — because the agreement presents the role as already filled, so nobody
|
||
looked for a decision they had to make.*
|
||
|
||
**The bedrock content records `annotating` as casting 6 of 6 roles, "a complete
|
||
protocol with no holes." That is wrong**, and it is wrong in the way that
|
||
matters: **an uncast role presented as cast is invisible**, which is the exact
|
||
failure entry 22's *yours to cast* state was added to prevent. *We built the
|
||
state and then mis-declared the first document we applied it to.*
|
||
|
||
*Concrete and small: **when `annotating` is next published, those two roles are
|
||
yours to cast**, and the external casting — `ETag` lock, replace-and-reconcile —
|
||
is the one an adopted agreement should use. **Which unfreezes both agreements as
|
||
a side effect**, three weeks after entry 30 first said they were stuck.*
|
||
|
||
### Recorded, not built on
|
||
|
||
**This is the strongest evidence so far that the bracketed model earns its
|
||
keep** — *a real bug, hit three times, diagnosed by the ontology in one table.*
|
||
|
||
**It changes nothing today.** loom said not to get lost in it and that is right:
|
||
**the finding is written down so it is waiting when problem 3 unbrackets**, and
|
||
**the fix it implies is a two-line change to one document, not a project.**
|
||
|
||
---
|
||
|
||
## 77. "Roles you must cast" is already a prompt block — no ontology required
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
what loom described — **loom owns `annotating`; `cart` and `externals` each cast
|
||
the filename, the lock and the dissolution** — is expressible **with the
|
||
mechanism `annotating` already has**, and needs nothing from problem 3.
|
||
|
||
**Advances** delivering, which is the thing at risk.
|
||
|
||
**Because** loom said it was sneaking back in. **It is not, and the reason is
|
||
that we already built this before any of it.** `annotating` says a file may ask
|
||
for structured answers with `Question:`, `Select:` and `Affirm:` headings, and
|
||
that answers land in the notes file.
|
||
|
||
> **"Roles you must cast" is a prompt block. "Yours to cast" is an unanswered
|
||
> prompt.** *`cart.md` has done this since before this round started, and the
|
||
> only thing missing is that `annotating` does not do it to itself.*
|
||
|
||
**So the whole structure is:**
|
||
|
||
```markdown
|
||
## Select: What is the sibling filename?
|
||
- `.notes.md`
|
||
- `.usages.md` and `.gaps.md`
|
||
|
||
## Select: What fixes the source?
|
||
- the existence of the notes file
|
||
- a lock on the publisher's ETag
|
||
|
||
## Question: How is it dissolved?
|
||
```
|
||
|
||
**`cart` answers one way. `externals` answers another.** *No beads, no stagings,
|
||
no roles — a document with questions, and adopters with answers.*
|
||
|
||
*And it fixes entry 76's bug without touching the model: **the adopted-agreement
|
||
case failed because nobody was asked.** A prompt asks.*
|
||
|
||
**Deliverable and small: `annotating/v1` gains three prompts; `cart` and
|
||
`externals` answer them.** *Two documents, no code, and both agreements unfreeze.*
|
||
|
||
---
|
||
|
||
## 78. The first useful version needs no server: a git host already does conditional GET
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: almanac`):
|
||
**problems 1 and 2 ship against a git host and two conventions.** The registry as
|
||
a service is what comes after, when browsing and gap aggregation are worth
|
||
building.
|
||
|
||
**Advances** something of value existing, which after three days of this is the
|
||
only remaining risk worth managing.
|
||
|
||
**Because** everything problem 1 needs, gitea already serves:
|
||
|
||
| need | already there |
|
||
|---|---|
|
||
| a document with an owner | a repository |
|
||
| an address | a raw URL |
|
||
| an `ETag` | conditional `GET` on raw files, or the commit sha |
|
||
| history | git |
|
||
| browsable rendering | gitea renders markdown |
|
||
|
||
**And the conventions are two:** *`externals/<host>/<path>.md` holds the pulled
|
||
copy; `.usages.md` and `.gaps.md` sit beside it.*
|
||
|
||
**Problem 2 is one file in bedrock** — *a list of things and their repositories.*
|
||
**A name lookup is a grep.**
|
||
|
||
> **What is genuinely not covered: aggregating gaps across repositories, and an
|
||
> entry point that explains itself.** *The first wants a service. The second is a
|
||
> README.*
|
||
|
||
*So the honest scope of the server, stated now rather than discovered later: **it
|
||
buys aggregation and a self-describing surface, and it buys them after the
|
||
conventions have been used enough to know their shape.** Entry 12 said there is a
|
||
server and that is unchanged — **this says what it is for, and that it is not
|
||
first.***
|
||
|
||
---
|
||
|
||
**Declined** (2026-09-07, loom + claude-substrate — `cart: almanac`): **a registry
|
||
service, first.** *Considered for three days.*
|
||
|
||
**Because** we believe a git host already provides a document with an owner, an
|
||
address, an `ETag`, history and rendering — **so the service buys aggregation and
|
||
a self-describing entry point, and buys them better after the conventions have
|
||
been used.** *Wrong if the conventions turn out to need a server to be usable at
|
||
all, which using them will show.*
|
||
|
||
---
|
||
|
||
**Declined** (2026-09-07, loom + claude-substrate — `cart: almanac`):
|
||
**affordances, stagings, roles and beads, for infrastructure and practice
|
||
description.** *Built out over three days and then bracketed.*
|
||
|
||
**Because** we believe the model is for describing software above its
|
||
implementation, and **that infrastructure and practice description is a different
|
||
altitude** being forced into it. *Wrong if the documentation layer turns out to
|
||
need decomposition to answer anything useful.*
|
||
|
||
---
|
||
|
||
**Declined** (2026-09-07, claude-substrate — `cart: almanac`): **bulk recasting a
|
||
bead across stagings.**
|
||
|
||
**Because** we believe the list is the migration plan — *resumable, observable,
|
||
and owned by whoever owns each staging* — where **a bulk operation is one
|
||
uninspectable moment that breaks every consumer at once.**
|
||
|
||
---
|
||
|
||
**Declined** (2026-09-07, claude-substrate — `cart: almanac`): **signatures.**
|
||
|
||
**Because** we believe a content hash catches every accident, and **a signature
|
||
answers an adversary that does not exist** — one tailnet, one operator. *Revisit
|
||
when a consumer must verify without being able to ask.*
|
||
|
||
---
|
||
|
||
**Declined** (2026-09-07, claude-substrate — `cart: almanac`): **`watch`, and any
|
||
subscription.**
|
||
|
||
**Because** we believe it would be **the first thing giving a registry persistent
|
||
knowledge of its consumers**, which is the line drawn at *a pin is not a
|
||
subscription* — and a conditional `GET` suffices for documents that change
|
||
monthly.
|
||
|
||
---
|
||
|
||
**Declined** (2026-09-07, claude-substrate — `cart: almanac`): **naming the thing
|
||
we kept reaching for** — *"accommodations", "standard".*
|
||
|
||
**Because** we believe it is **a lifecycle stage with a measurement rather than a
|
||
kind needing a name**: *a specification is a thing that names roles and casts none
|
||
of them.* **Coin it when we keep needing to say it**, which is a better reason
|
||
than noticing a gap.
|
||
|
||
---
|
||
|
||
**Declined** (2026-09-07, claude-substrate — `cart: almanac`): **an `internal/`
|
||
directory.**
|
||
|
||
**Because** we believe naming the complement of `published/` invents a category —
|
||
**and one called `internal/` would look like a security boundary and not be
|
||
one.**
|
||
|
||
---
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: kestrel`):
|
||
**a decline is an entry.** `.loom/declines.md` is removed and its seven entries
|
||
are above.
|
||
|
||
**Advances** one record rather than two, and the ordering between *what we did*
|
||
and *what we considered and did not* being visible.
|
||
|
||
**Because** loom asked whether `declines.md` was a thing or had drifted. **It had
|
||
drifted** — written during conversion because the cart agreement says a round
|
||
emits the artifact **and** the declines, and made a separate file without checking
|
||
against the primitive published in this repository an hour later. *Which says, in
|
||
so many words, **declines belong here too.***
|
||
|
||
> **A decline is a decision: what you considered and did not do, with the belief
|
||
> that could later be shown wrong.** *Splitting them loses the ordering, and
|
||
> makes a reader know to look in two places.*
|
||
|
||
*The general form, worth having: **a second file for a special case of something
|
||
that already has a home is drift**, and it is easiest to catch by reading your own
|
||
published rules back.*
|
||
|
||
---
|
||
|
||
**Decided** (2026-09-07, loom + claude-substrate — `discovered`, `cart: osprey`):
|
||
**the starting page no longer links to the worked example**, because the example
|
||
is private and this page is not.
|
||
|
||
**Advances** a public document not promising something its reader cannot reach.
|
||
|
||
**Because** `loom-cli`'s builder measured all three things we had published and
|
||
**none of them could be fetched by somebody who is not us:**
|
||
|
||
```
|
||
loom/settled 404 anon the page written to fix "settled is private"
|
||
loom/.loom 200 anon fetchable, but not where a reader lands
|
||
jeffry/homelab-cluster 404 anon and this page pointed at it, publicly
|
||
```
|
||
|
||
**Three for three.** *Every time the question **can somebody who is not you fetch
|
||
this** was asked, the answer was no.*
|
||
|
||
> **A public page naming a private thing as its canonical answer is worse than no
|
||
> example.** *It costs the reader a request and their confidence in the rest of
|
||
> the page.*
|
||
|
||
*The diagnosis is theirs and it is the one to keep: **publication was being
|
||
treated as a change of directory rather than as the act `publication.md` says it
|
||
is.** Moving a file into `published/` is not publishing. **The file becoming
|
||
fetchable is.***
|
||
|
||
*And the command we nearly cut for being a third kind of thing — `reachable` —
|
||
found all three on the day it would have shipped. **That is its validation and not
|
||
a feature request.***
|
||
|
||
---
|
||
|
||
## 79 — publication says that a publication nobody can fetch is not one
|
||
|
||
**Decided.** `publication.md` gains *"publishing is not an act you can complete
|
||
alone"* as a property of the act, and says that publication has a visibility and
|
||
that naming the readers is part of publishing.
|
||
|
||
**Advances:** a repository can tell whether it has actually published.
|
||
|
||
**Filed by** `loom-cli` in `publication.gaps.md`, from one round of real use. Their
|
||
claim: the sentence we needed already existed, but **in a sibling document, as an
|
||
aside about a tool**, rather than as a property of publication — and
|
||
`publication.md` never mentioned visibility although `externals`' confidentiality
|
||
rule depends on it entirely.
|
||
|
||
**The belief that could turn out false:** that "the test is a fetch you did not
|
||
perform" is checkable often enough to be a practice and not just a slogan. *It was
|
||
measured three times in one afternoon and failed all three, which is evidence it
|
||
is worth stating and no evidence at all that anybody will do it.*
|
||
|
||
## 80 — compaction is allowed, and four kinds of entry are exempt
|
||
|
||
**Decided.** `recording-decisions.md` gains a compaction rule: an entry may be
|
||
dropped when a competent reader could recover it from the artifact, and may never
|
||
be dropped when it records a decline, a measurement, a belief shown wrong, or a
|
||
supersession. A compaction gets an entry.
|
||
|
||
**Advances:** the log stays readable without losing the part that has no other
|
||
home.
|
||
|
||
**Filed by** `loom-cli` in `recording-decisions.gaps.md` — that *"never revised"*
|
||
and *"somebody must be able to read it"* both hold and eventually conflict. **The
|
||
rule adopted here is theirs, including its formulation:** *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.*
|
||
|
||
**The belief that could turn out false:** that "recoverable from the artifact" is
|
||
a judgement two people make the same way. *If it is not, compaction becomes a
|
||
quiet way to lose entry 69.*
|
||
|
||
## 81 — the four gap files are answered, and one was answered by repair
|
||
|
||
**Decided.** Of the four gap files `loom-cli` filed against the four
|
||
conventions — the first genuine use of `externals` on a real dependency — three
|
||
are answered: entries 79 and 80 here, and `cart/v2`.
|
||
|
||
**`annotating` is left standing**, deliberately. *Its gaps are real* — the source
|
||
hash that detects a violated freeze, **dissolve** as the definition of responding,
|
||
and what to do when a frozen file must change — *but `annotating` is the
|
||
most-depended-on document we have and the one whose revision costs every adopter
|
||
an afternoon.* **Jeff's call, not mine.**
|
||
|
||
**The measurement worth keeping:** *the adopter found four defects in four
|
||
documents in three rounds, and the sharpest one was in the version I had written
|
||
that same day.* **`cart/v1` made the cart untracked and did not notice that this
|
||
turns conversion from lossy-in-the-tree into lossy-absolutely.**
|
||
|
||
**Which is the case for `externals` as a whole**, stated better by the thing than
|
||
by the document: *the gap file is the only mechanism here where somebody who
|
||
cannot edit your document can still put a defect in front of you, and it caught
|
||
what review by the author did not.*
|