diff --git a/.loom/externals/.locks b/.loom/externals/.locks index 91eab3c..f638d5c 100644 --- a/.loom/externals/.locks +++ b/.loom/externals/.locks @@ -9,6 +9,7 @@ git.hypertheory-labs.dev/loom/bedrock/publication.md https://git.hypertheory-lab git.hypertheory-labs.dev/loom/bedrock/recording-decisions.md https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/recording-decisions.md "d190eeae7929b9a99ecf09211d94e7e5b32635bd" public git.hypertheory-labs.dev/loom/bedrock/sibling-facets.md https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/sibling-facets.md "a46446a34ccb8bfc533d3cce19f4c88548c4fa04" public git.hypertheory-labs.dev/loom/bedrock/starting.md https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/starting.md "b9eefba0f4668a496ccfc6a1377277f0721456d2" public +git.hypertheory-labs.dev/loom/bedrock/venues.md https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/venues.md "35e940195e2ba9a30b4b4dc81a57f9383c709f64" public git.hypertheory-labs.dev/loom/cart/cart.md https://git.hypertheory-labs.dev/loom/cart/raw/branch/main/.loom/published/cart.md "15331f1a9cc81bf61a44830cfbb7c274f4c2b119" public -git.hypertheory-labs.dev/loom/externals/externals.md https://git.hypertheory-labs.dev/loom/externals/raw/branch/main/.loom/published/externals.md "50673ccffc14d57150ac0a9027b0712d9dcf940d" +git.hypertheory-labs.dev/loom/externals/externals.md https://git.hypertheory-labs.dev/loom/externals/raw/branch/main/.loom/published/externals.md "772c67f460f0ff3b9563aee9b38a64c0f3423dbd" git.hypertheory-labs.dev/loom/loom-cli/guarantees.md https://git.hypertheory-labs.dev/loom/loom-cli/raw/branch/main/.loom/published/guarantees.md "e8c697ea61df47838f38af7353e29d9ae3f7070c" diff --git a/.loom/externals/git.hypertheory-labs.dev/loom/bedrock/venues.md b/.loom/externals/git.hypertheory-labs.dev/loom/bedrock/venues.md new file mode 100644 index 0000000..35e9401 --- /dev/null +++ b/.loom/externals/git.hypertheory-labs.dev/loom/bedrock/venues.md @@ -0,0 +1,81 @@ +# Venues + +**`.loom/venues/.md` — a choice you made about something that has no +document of its own to hang it on.** + +*Kafka. React. HTTP. The organisation you work for.* **Things that constrain what +you build without ever being invoked, and without publishing anything at you that +you could adopt.** + +--- + +## Venue and external are two questions, not two kinds of thing + +**`external` is *who published it*. `venue` is *whether you had to decide +anything about it*.** *They are orthogonal. A thing may be both, either, or +neither.* + +| | published at us | we recorded a choice | files | +|---|---|---|---| +| Kafka, React, HTTP | yes, by the world | yes — *protobuf for schema* | venue only | +| a service another team owns | yes, by them | not yet — the use is in the code | external only | +| a cluster your own org publishes | yes | yes | both | +| a library you use exactly as documented | yes | no | neither | + +**Both files are governed by the same rule, applied to two different ambients:** + +> **An external entry exists when the reader cannot be assumed to know the +> thing.** *A venue file exists when you 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 your internal 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 is why sorting things into two bins does not work and people keep trying. +The bins were never the problem. The question was.* + +## A venue file is an account of your own decisions + +**Which is what makes it local by construction.** *An external is a copy of +somebody's document and could in principle be centralised.* **A venue file has no +upstream** — *there is nobody to reconcile with, because the only party who +decided anything was you.* + +**So it does not go stale the way a copy does.** *It goes stale when you change +your mind and do not say so*, which no fetch will ever detect. + +> **A venue file that carries no delta should not exist.** *If you use the thing +> exactly as documented, write nothing.* **Recording the adoption is ceremony.** + +## If there is a document, hang the choice on the document + +**Put a choice where reconciliation will look for it.** *When the thing you +decided about publishes something you have adopted, the choice belongs in the +`.usages.md` beside that copy and not in a venue file* — **because that is the +file somebody opens when the document moves**, *and a venue file is not.* + +*The venue file is for what is left: the constraints of a thing that publishes +nothing you can hold.* + +## A venue is where an authority claim belongs + +**Because a venue fact carries a reason and can therefore be shown wrong.** + +*The alternative is worse and common: somebody with authority enters a +conversation as a voice, and "we do it this way" arrives with no reason attached +and nothing that could contradict it.* **Written as a venue constraint, the same +instruction becomes a claim about the world**, *and a claim about the world is +something a later reader can discover was false.* + +## Checking it is still true + +**Nobody will tell you.** *There is no publisher to ask, no `ETag`, and no +conditional request that will ever come back `200`.* + +**So a venue file needs what a published document needs: a date, and the reason +underneath the choice.** *The reason is the whole of it* — **"we use protobuf for +schema" is unfalsifiable a year later; "we use protobuf because the consumers are +in three languages and JSON schema drift was costing us a release a quarter" can +be checked against a world that may have changed.** diff --git a/.loom/externals/git.hypertheory-labs.dev/loom/externals/externals.md b/.loom/externals/git.hypertheory-labs.dev/loom/externals/externals.md index 50673cc..772c67f 100644 --- a/.loom/externals/git.hypertheory-labs.dev/loom/externals/externals.md +++ b/.loom/externals/git.hypertheory-labs.dev/loom/externals/externals.md @@ -147,13 +147,16 @@ do; a closed gap is what you needed and got.* **The new copy replaces the old pair wholesale.** *There is no merging a document you do not own.* -## Venues, for things you cannot fetch +## A choice about something you adopted does not go in a venue file -**`.loom/venues/.md`** — *Kafka, React, HTTP.* **Choices we made about -something with no document to hang them on.** +**Put a choice where reconciliation will look for it.** *When the thing you +decided about publishes a document you hold a copy of, the choice belongs in the +`.usages.md` beside that copy* — **because that is the file somebody opens when +the document moves.** -*If there is a document, hang it there instead: **put a choice where -reconciliation will look for it.*** +*Venues are for what is left: things that constrain you without publishing +anything you could adopt.* **See +[venues](https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/venues.md).** ## Gaps that belong to nobody diff --git a/scripts/generate.mjs b/scripts/generate.mjs index abeacac..a2af82e 100644 --- a/scripts/generate.mjs +++ b/scripts/generate.mjs @@ -154,13 +154,25 @@ function guidePage(repo, records, existing) { let data = { title: repo, loom: {} } let body = GUIDE_TEMPLATE(repo) let stale = [] + let changed = [] + let added = [] + let gone = [] if (existing) { const split = splitFrontmatter(existing) data = { ...split.data } body = split.body const was = new Map((data.loom?.writtenAgainst ?? []).map((s) => [s.path, s.etag])) - stale = records.filter((r) => was.has(r.path) && was.get(r.path) !== r.etag).map((r) => r.name) + // A section gaining or losing a document changes the guide too, and this + // did not notice until bedrock grew a fifth page under a guide that + // enumerates its pages by hand. Comparing only what is present in both is a + // comparison that cannot see an arrival. + changed = records.filter((r) => was.has(r.path) && was.get(r.path) !== r.etag).map((r) => r.name) + added = records.filter((r) => !was.has(r.path)).map((r) => r.name) + gone = [...was.keys()] + .filter((p) => !records.some((r) => r.path === p)) + .map((p) => p.split('/').pop().replace(/\.md$/, '')) + stale = [...changed, ...added, ...gone] } data.title ??= repo @@ -175,9 +187,16 @@ function guidePage(repo, records, existing) { // only ever know that they differ. The first time it fired in real use, the // guide was written against a version newer than the copy here, and a // message claiming "an older X" was confidently backwards. - const which = stale.map((s) => `${s}`).join(', ') + const code = (xs) => xs.map((x) => `${x}`).join(', ') + const parts = [] + if (changed.length) + parts.push( + `${code(changed)} differs from the version this page was written against — so one of the two is behind`, + ) + if (added.length) parts.push(`${code(added)} was adopted after this page was written`) + if (gone.length) parts.push(`${code(gone)} is no longer adopted`) data.banner = { - content: `Written against a different ${which} than the copy in this repository — so one of the two is behind. Run loomctl external check, or re-read this page, then npm run ack.`, + content: `${parts.join('; ')}. Run loomctl external check, or re-read this page, then npm run ack.`, } } else { delete data.banner diff --git a/src/content/docs/bedrock/index.mdx b/src/content/docs/bedrock/index.mdx index e520e68..26ef225 100644 --- a/src/content/docs/bedrock/index.mdx +++ b/src/content/docs/bedrock/index.mdx @@ -12,6 +12,8 @@ loom: etag: '"a46446a34ccb8bfc533d3cce19f4c88548c4fa04"' - path: git.hypertheory-labs.dev/loom/bedrock/starting.md etag: '"b9eefba0f4668a496ccfc6a1377277f0721456d2"' + - path: git.hypertheory-labs.dev/loom/bedrock/venues.md + etag: '"35e940195e2ba9a30b4b4dc81a57f9383c709f64"' --- Read [starting](/bedrock/starting/) first — it is two questions and a directory, @@ -29,6 +31,20 @@ repository starts depending on you: by whoever wrote the facet. - [loom-directory](/bedrock/loom-directory/) — type by location. A stream is a file; a collection of things with identity is a directory. +- [venues](/bedrock/venues/) — a choice you made about something that publishes + nothing you could adopt. Kafka, React, the organisation you work for. + +## Venue and external are two questions, not two bins + +Worth reading [venues](/bedrock/venues/) alongside +[externals](/externals/externals/), because sorting things into two kinds is the +mistake almost everybody makes first, and we made it for a day. + +**`external` is who published it. `venue` is whether you had to decide anything +about it.** They are orthogonal, so a thing can be both, either, or neither — +and the same rule governs both files: record only the delta. Nobody needs telling +what Kafka is, so there is no external entry; *we use protobuf for schema* is a +choice, so there is a venue file. ## The one that gets skipped diff --git a/src/content/docs/bedrock/venues.md b/src/content/docs/bedrock/venues.md new file mode 100644 index 0000000..da68bd1 --- /dev/null +++ b/src/content/docs/bedrock/venues.md @@ -0,0 +1,98 @@ +--- +title: Venues +editUrl: false +loom: + generated: true + path: git.hypertheory-labs.dev/loom/bedrock/venues.md + source: https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/venues.md + etag: '"35e940195e2ba9a30b4b4dc81a57f9383c709f64"' + visibility: public +--- +**`.loom/venues/.md` — a choice you made about something that has no +document of its own to hang it on.** + +*Kafka. React. HTTP. The organisation you work for.* **Things that constrain what +you build without ever being invoked, and without publishing anything at you that +you could adopt.** + +--- + +## Venue and external are two questions, not two kinds of thing + +**`external` is *who published it*. `venue` is *whether you had to decide +anything about it*.** *They are orthogonal. A thing may be both, either, or +neither.* + +| | published at us | we recorded a choice | files | +|---|---|---|---| +| Kafka, React, HTTP | yes, by the world | yes — *protobuf for schema* | venue only | +| a service another team owns | yes, by them | not yet — the use is in the code | external only | +| a cluster your own org publishes | yes | yes | both | +| a library you use exactly as documented | yes | no | neither | + +**Both files are governed by the same rule, applied to two different ambients:** + +> **An external entry exists when the reader cannot be assumed to know the +> thing.** *A venue file exists when you 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 your internal 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 is why sorting things into two bins does not work and people keep trying. +The bins were never the problem. The question was.* + +## A venue file is an account of your own decisions + +**Which is what makes it local by construction.** *An external is a copy of +somebody's document and could in principle be centralised.* **A venue file has no +upstream** — *there is nobody to reconcile with, because the only party who +decided anything was you.* + +**So it does not go stale the way a copy does.** *It goes stale when you change +your mind and do not say so*, which no fetch will ever detect. + +> **A venue file that carries no delta should not exist.** *If you use the thing +> exactly as documented, write nothing.* **Recording the adoption is ceremony.** + +## If there is a document, hang the choice on the document + +**Put a choice where reconciliation will look for it.** *When the thing you +decided about publishes something you have adopted, the choice belongs in the +`.usages.md` beside that copy and not in a venue file* — **because that is the +file somebody opens when the document moves**, *and a venue file is not.* + +*The venue file is for what is left: the constraints of a thing that publishes +nothing you can hold.* + +## A venue is where an authority claim belongs + +**Because a venue fact carries a reason and can therefore be shown wrong.** + +*The alternative is worse and common: somebody with authority enters a +conversation as a voice, and "we do it this way" arrives with no reason attached +and nothing that could contradict it.* **Written as a venue constraint, the same +instruction becomes a claim about the world**, *and a claim about the world is +something a later reader can discover was false.* + +## Checking it is still true + +**Nobody will tell you.** *There is no publisher to ask, no `ETag`, and no +conditional request that will ever come back `200`.* + +**So a venue file needs what a published document needs: a date, and the reason +underneath the choice.** *The reason is the whole of it* — **"we use protobuf for +schema" is unfalsifiable a year later; "we use protobuf because the consumers are +in three languages and JSON schema drift was costing us a release a quarter" can +be checked against a world that may have changed.** + + +
+ +This page is a copy of a document published by `loom/bedrock`, rendered here. +The source is [https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/venues.md](https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/venues.md) and is what the copy is checked against. + + +
diff --git a/src/content/docs/externals/externals.md b/src/content/docs/externals/externals.md index d74c254..9c01916 100644 --- a/src/content/docs/externals/externals.md +++ b/src/content/docs/externals/externals.md @@ -5,7 +5,7 @@ loom: generated: true path: git.hypertheory-labs.dev/loom/externals/externals.md source: https://git.hypertheory-labs.dev/loom/externals/raw/branch/main/.loom/published/externals.md - etag: '"50673ccffc14d57150ac0a9027b0712d9dcf940d"' + etag: '"772c67f460f0ff3b9563aee9b38a64c0f3423dbd"' visibility: unknown --- **How a repository records what it depends on, and finds out when it changes.** @@ -155,13 +155,16 @@ do; a closed gap is what you needed and got.* **The new copy replaces the old pair wholesale.** *There is no merging a document you do not own.* -## Venues, for things you cannot fetch +## A choice about something you adopted does not go in a venue file -**`.loom/venues/.md`** — *Kafka, React, HTTP.* **Choices we made about -something with no document to hang them on.** +**Put a choice where reconciliation will look for it.** *When the thing you +decided about publishes a document you hold a copy of, the choice belongs in the +`.usages.md` beside that copy* — **because that is the file somebody opens when +the document moves.** -*If there is a document, hang it there instead: **put a choice where -reconciliation will look for it.*** +*Venues are for what is left: things that constrain you without publishing +anything you could adopt.* **See +[venues](https://git.hypertheory-labs.dev/loom/bedrock/raw/branch/main/.loom/published/venues.md).** ## Gaps that belong to nobody diff --git a/src/content/docs/externals/index.mdx b/src/content/docs/externals/index.mdx index 322796a..ecf5e53 100644 --- a/src/content/docs/externals/index.mdx +++ b/src/content/docs/externals/index.mdx @@ -3,7 +3,7 @@ title: externals loom: writtenAgainst: - path: git.hypertheory-labs.dev/loom/externals/externals.md - etag: '"50673ccffc14d57150ac0a9027b0712d9dcf940d"' + etag: '"772c67f460f0ff3b9563aee9b38a64c0f3423dbd"' --- A copy of somebody's document, a lock recording where it came from, and — beside @@ -38,6 +38,14 @@ request cannot tell two repositories private to *different people* apart — and that is precisely the case where private-into-private genuinely widens access. Record the word you can verify. +## What does not go in a `.usages.md` + +A choice about something you have *not* adopted — Kafka's wire format, the +organisation's policy — is a [venue](/bedrock/venues/), not a usage. This +document used to define venues in four lines and no longer does, which is the +right way round: put a choice where reconciliation will look for it, and a +`.usages.md` is what somebody opens when a document moves. + ## Why this site can only hold public documents *Do not adopt from a source less readable than the repository you are adopting