From e325fd605231d59a91642772b8f3440b06308979 Mon Sep 17 00:00:00 2001 From: Robert Blust Date: Sun, 6 Sep 2026 14:33:44 +0200 Subject: [PATCH] Conventions v1.5.0, with the writer and translator adapters The release adds the two role briefs and the glossary to the vendored files and writes the writing process into WRITING.md. Both tags move, sync brings the six files and the block, and the two agent adapters from the README recipe join .claude/agents/, written once like CLAUDE.md. Verified: sh conventions/conventions-sync check and sh conventions/conventions-check pass; python3 verify.py passes. Co-Authored-By: Claude Fable 5.1 --- .claude/agents/translator.md | 6 ++++ .claude/agents/writer.md | 6 ++++ .github/workflows/conventions.yml | 2 +- AGENTS.md | 4 ++- conventions.json | 2 +- conventions/AGENTS.md | 21 ++++++++------ conventions/GLOSSARY.md | 31 +++++++++++++++++++++ conventions/TRANSLATOR.md | 46 +++++++++++++++++++++++++++++++ conventions/WORKING.md | 8 ++++-- conventions/WRITER.md | 36 ++++++++++++++++++++++++ conventions/WRITING.md | 41 +++++++++++++++++++++++---- conventions/conventions-sync | 2 +- conventions/manifest.json | 13 +++++---- 13 files changed, 191 insertions(+), 27 deletions(-) create mode 100644 .claude/agents/translator.md create mode 100644 .claude/agents/writer.md create mode 100644 conventions/GLOSSARY.md create mode 100644 conventions/TRANSLATOR.md create mode 100644 conventions/WRITER.md diff --git a/.claude/agents/translator.md b/.claude/agents/translator.md new file mode 100644 index 0000000..af69254 --- /dev/null +++ b/.claude/agents/translator.md @@ -0,0 +1,6 @@ +--- +name: translator +description: Makes the Swiss German of elements whose English the owner has reviewed, in the -de attribute or the de branch the page uses, and hands back a back-translation per element. Use it only after the English review, never on a draft. +tools: Read, Grep, Glob, Edit +--- +Read `conventions/WRITING.md`, `conventions/GLOSSARY.md` and `conventions/TRANSLATOR.md` before anything else, and follow them. Report in the reply register of `WRITING.md`: one row per element with the English, the German and the German read back into plain English, and any element you left because its English is not reviewed. diff --git a/.claude/agents/writer.md b/.claude/agents/writer.md new file mode 100644 index 0000000..feaf8f0 --- /dev/null +++ b/.claude/agents/writer.md @@ -0,0 +1,6 @@ +--- +name: writer +description: Drafts or revises English text in the family voice from a brief — a page, a README, release notes, an agent file. Use it whenever a task is to write or rewrite prose rather than code. +tools: Read, Grep, Glob, Edit +--- +Read `conventions/WRITING.md`, `conventions/GLOSSARY.md` and `conventions/WRITER.md` before anything else, and follow them. Report in the reply register of `WRITING.md`: what you wrote, what you changed and which claims you could not trace to the brief or the repository. diff --git a/.github/workflows/conventions.yml b/.github/workflows/conventions.yml index b3d5135..287c22a 100644 --- a/.github/workflows/conventions.yml +++ b/.github/workflows/conventions.yml @@ -5,4 +5,4 @@ on: pull_request: jobs: conventions: - uses: robertblust/conventions/.github/workflows/check.yml@v1.4.0 + uses: robertblust/conventions/.github/workflows/check.yml@v1.5.0 diff --git a/AGENTS.md b/AGENTS.md index d69df72..b47cd0d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ - + Shared conventions of the robertblust, guestgraph and companygraph organizations live in `conventions/`, vendored from robertblust/conventions at the release `conventions.json` names. Read them before writing or committing anything here. @@ -6,6 +6,8 @@ names. Read them before writing or committing anything here. - `conventions/WRITING.md` — how we write: one voice, three registers, English and German. - `conventions/WORKING.md` — how we work with git and GitHub. - `conventions/REPOSITORIES.md` — the family: what each repository is and what pins what. +- `conventions/WRITER.md`, `conventions/TRANSLATOR.md`, `conventions/GLOSSARY.md` — the two roles that + make a text, and the terms they keep. Everything below this block is this repository's own. `sh conventions/conventions-sync check` says whether the copy matches the release, `sync` brings it to the release the pin names, and diff --git a/conventions.json b/conventions.json index 1c223d4..2fd2b82 100644 --- a/conventions.json +++ b/conventions.json @@ -1 +1 @@ -{ "repo": "robertblust/conventions", "tag": "v1.4.0" } +{ "repo": "robertblust/conventions", "tag": "v1.5.0" } diff --git a/conventions/AGENTS.md b/conventions/AGENTS.md index 911bdf8..51a96db 100644 --- a/conventions/AGENTS.md +++ b/conventions/AGENTS.md @@ -1,4 +1,4 @@ - + Shared conventions of the robertblust, guestgraph and companygraph organizations live in `conventions/`, vendored from robertblust/conventions at the release `conventions.json` names. Read them before writing or committing anything here. @@ -6,6 +6,8 @@ names. Read them before writing or committing anything here. - `conventions/WRITING.md` — how we write: one voice, three registers, English and German. - `conventions/WORKING.md` — how we work with git and GitHub. - `conventions/REPOSITORIES.md` — the family: what each repository is and what pins what. +- `conventions/WRITER.md`, `conventions/TRANSLATOR.md`, `conventions/GLOSSARY.md` — the two roles that + make a text, and the terms they keep. Everything below this block is this repository's own. `sh conventions/conventions-sync check` says whether the copy matches the release, `sync` brings it to the release the pin names, and @@ -15,14 +17,15 @@ a shared file in robertblust/conventions, never here. # robertblust/conventions — working conventions -This repository is the source of the block above. It mirrors the layout it vendors: the -shared files live under `conventions/` here exactly as they do in every member, so the block -reads the same in both. The block is plain words and names no agent vendor; `CLAUDE.md` is the -one vendor adapter, four lines that import the entry file and the three shared files in that -vendor's syntax, and a member carries the same four lines. The one file members receive that -does not sit under `conventions/` in the source is this `AGENTS.md`, which the script fetches -from the root and vendors as `conventions/AGENTS.md` so that `check` can compare a member's -block against the release without a network. +This repository is the source of the block above. It mirrors the layout it vendors: the shared +files live under `conventions/` here exactly as they do in every member, so the block reads the +same in both. The block is plain words and names no agent vendor; `CLAUDE.md` is the one vendor +adapter, four lines that import the entry file and `WRITING.md`, `WORKING.md` and +`REPOSITORIES.md` in that vendor's syntax, and a member carries the same four lines; the two +briefs and the glossary are read by the agent adapters the README names, not by every session. +The one file members receive that does not sit under `conventions/` in the source is this +`AGENTS.md`, which the script fetches from the root and vendors as `conventions/AGENTS.md` so +that `check` can compare a member's block against the release without a network. Releasing is a tag and a GitHub Release with notes. Before tagging, set the version in the first line of this file to the new tag: the script rewrites it to the pin on sync, so a stale diff --git a/conventions/GLOSSARY.md b/conventions/GLOSSARY.md new file mode 100644 index 0000000..dd679c7 --- /dev/null +++ b/conventions/GLOSSARY.md @@ -0,0 +1,31 @@ +# Glossary + +Every family term in its fixed English and German form. The writer uses the English column +so the third site says what the first says; the translator uses the German column so one +word has one rendering across the sites. A term joins the table the first time a text needs +it and the translator has to choose, and the owner's choice is recorded here rather than in +the attribute where it was first made. + +The German cells are inline code because the prose check reads no language and a German word +such as `Organisation` would be a hit. This table, the German paragraph of `WRITING.md` and the +marks `TRANSLATOR.md` repeats are the only places the shared files carry German on purpose. + +| Term | English | German | Note | +|---|---|---|---| +| CompanyGraph | CompanyGraph | `CompanyGraph` | A name; one word, two capitals, in both languages. | +| GuestGraph | GuestGraph | `GuestGraph` | A name, as above. | +| meta-model | meta-model | `Meta-Modell` | Hyphen and lower case in English; hyphen and two capitals in German. Every site carries this form. | +| guest identity graph | guest identity graph | `Identitätsgraph für Gäste` | guestgraph.io's own phrase; `Open-Source-Identitätsgraph` where the sentence says open source. | +| identity resolution | identity resolution | `Identitätsauflösung` | guestgraph.io, the billing page and the intro talk. | +| Mental Model | Mental Model | `Mental Model` | A coined name for the owner's knowledge base, kept in English in both languages like CompanyGraph; `mentales Modell` is the psychology term and is not meant. The page `/model/` is `Modell`, the generic noun. | +| open core | open core | `Open Core` | Kept in English on blust.ch; a term of the trade. | +| open source | open source | `quelloffen` | The adjective; capitalized `Quelloffen` where it stands alone as a tag; in a compound, `Open-Source-`, as guestgraph.io writes it. Never the bare English `Open Source` in German. | +| talk | talk | `Vortrag` | A talk on a site; the deck is the file that carries it. | +| reference instance | reference instance | `Referenz-Instanz` | mental-model's role in the family; the form blust.ch's ideas page chose. | +| guest graph | guest graph | `Gast-Graph` | The short form, distinct from guest identity graph; blust.ch's ideas page. | +| core | core | `core` | The directory in companygraph/meta-model and the release its version names; a name the ecosystem reads, so not `Kern`. | +| considered, not accepted | considered, not accepted | `Erwogen, nicht angenommen` | The design system's phrase for a candidate weighed and set aside; one form on every billing page. | +| figure | figure | `Diagramm` | The drawn graph on a model page. The owner's choice over `Zeichnung`, which reads as a drawing by hand. | + +English forms fixed here whose German no page carries yet, to be chosen the first time a text +needs them: pack, design system, deck. diff --git a/conventions/TRANSLATOR.md b/conventions/TRANSLATOR.md new file mode 100644 index 0000000..3f5ab60 --- /dev/null +++ b/conventions/TRANSLATOR.md @@ -0,0 +1,46 @@ +# Translator + +The role that makes the German of an element whose English the owner has reviewed. It exists +because the German of the pages is a translation made by an agent and read by nobody as +German, so the discipline has to sit in the role: reviewed English in, German and its +back-translation out, and the same word for the same thing on every site. `WRITING.md` is +its rulebook, its German section above all, and `GLOSSARY.md` fixes every family term. + +## What it takes + +The reviewed English of the elements named in the task, and `GLOSSARY.md`. Reviewed means the +owner has said the English is done; a draft is not reviewed, and the translator says so in its +reply rather than assuming. The task names the elements; where it names none, the translator +names the gap in its reply and writes nothing, because it has no view of a diff and a guessed +scope is a wrong one. A page's own agent file says where that page carries German: the `-de` +attribute on an element, the `de:` branch of a `UI` or `TALK` object, a `translates` spec. + +## What it produces + +The German in the element's own place, with the markup inside the attribute kept and only its +text replaced, in the quote character the attribute already uses. And a reply in the reply +register with one row per element: the English, the German, and the German read back into +plain English by the translator, so the owner reads meaning in a minute without reading +German. Where German needed a different number of sentences than the English, the row says so. + +## What it never does + +It never changes an English word. It never translates an element whose English is not +reviewed. It never renders a glossary term in any form but the glossary's. It never writes a +`-de` on a page whose note says the model's own words stay English in both views. It never +commits. + +## How the German is written + +As the German section of `WRITING.md` says: Sie, never du; ss, never ß; «aussen» and ‹innen›; +the spaced en-dash – like this – and never an em-dash; 4. Mai 2012, Jan., Febr., März, Apr., +Mai, Juni, Juli, Aug., Sept., Okt., Nov., Dez.; 16’000 with the typographic apostrophe, which +cannot end a single-quoted attribute. German syntax, not English syntax in German words: +shorter sentences where German would stack clauses, the verb where German puts it, a noun +where English reached for a gerund. + +## Before it reports + +The back-translation of each row means what the English means. Every glossary term is in its +form. The marks are the German section's. The attribute's quote character does not appear in +its value. No English word changed. diff --git a/conventions/WORKING.md b/conventions/WORKING.md index f3e0744..04bf068 100644 --- a/conventions/WORKING.md +++ b/conventions/WORKING.md @@ -12,11 +12,13 @@ suite, and a ruleset that forbids a push is the only kind that survives a hurrie A branch is deleted once its pull request is merged. The merge commit is its record; a branch left standing is a question every reader of the branch list has to answer again. -An agent commits when the owner asks, and not on its own initiative. It proposes the message -in the git register of `WRITING.md`. The author of the commit is the person. A tool that +An agent commits when the owner asks, and not on its own initiative. It proposes the message in +the git register of `WRITING.md`. The author of the commit is the person. A tool that co-authored the change is named in a `Co-Authored-By` trailer, whichever tool it was, so the history says who and what wrote it. “Commit and open the pull request” is a request to do -exactly that; it is not approval to merge. +exactly that; it is not approval to merge. A role invoked as a subagent, the writer of +`WRITER.md` or the translator of `TRANSLATOR.md`, edits files and reports; it never commits, +and the session that invoked it proposes the message. ## Pull requests diff --git a/conventions/WRITER.md b/conventions/WRITER.md new file mode 100644 index 0000000..82dda88 --- /dev/null +++ b/conventions/WRITER.md @@ -0,0 +1,36 @@ +# Writer + +The role that drafts English in the family voice: a page, a README, release notes, an agent +file. It exists so that the voice is the same on the third site as on the first, and so that +the owner reviews a draft instead of writing one. `WRITING.md` is its rulebook, the section +*How a text is made* its place in the order, and `GLOSSARY.md` the form of every family term. + +## What it takes + +A brief. The brief names the audience, the one point the text makes, the facts the text may +claim and where each is shown, and the file and the place in it where the text lands. Where the +brief lacks the audience, the point or a fact's source, the writer names the gap in its reply +and writes nothing until it has it; a guessed audience produces a text for nobody, and a +guessed fact is the sentence the next reviewer has to unpick. + +## What it produces + +The text, in the file, on the current branch, in the register the place calls for: the prose +register for a page or a README, its page paragraph for a landing page, release notes shaped +as `WRITING.md` says. And a reply in the reply register: what it wrote, what it changed, and +which claims it could not trace to the brief or to the repository, each named so the reviewer +can strike or source it. + +## What it never does + +It never writes a fact or a number the brief or the repository does not show. It never writes +into a `-de` attribute, a `de:` branch or a `translates` spec; the German is the translator's, +made after the English is reviewed. It never commits and never runs the build; the session that +invoked it does both when the owner asks. It never uses an adjective that sells. + +## Before it reports + +The first sentence of every section carries the point. Each sentence carries one idea. Every +claim has a source in the brief or the repository, or is named in the reply as unsourced. The +marks are the English section's: spaced em-dash, curly quotes, no serial comma, May 4, 2012. +Every family term is in the form `GLOSSARY.md` gives it. diff --git a/conventions/WRITING.md b/conventions/WRITING.md index d51582c..22ab310 100644 --- a/conventions/WRITING.md +++ b/conventions/WRITING.md @@ -12,12 +12,35 @@ commit messages, pull requests, release notes and what an agent says. English he the section on English below says what that means in spelling and marks. The pages carry a second language, Swiss German, de-CH. It is there because the person behind -the family is Swiss, and a reader in Zürich or Bern is a reader the pages are written for; it -is a second original, not a translation made for a market. So the German is written as -German, in the forms Switzerland uses, by someone who reads it, and it is never generated and -left unread. It appears only where a page carries it, in the value of an attribute whose name -ends in `-de`, and nowhere else: not in code, not in git, not in a reply. A third language is -not planned; adding one would mean writing this paragraph again with its own reason. +the family is Swiss, and a reader in Zürich or Bern is a reader the pages are written for. The +German is a translation of the English, made after the owner has reviewed the English and never +before, by the translator role of `TRANSLATOR.md`, in the forms Switzerland uses. It is +reviewed by reading the translator's back-translation, an English rendering of what the German +says, because that takes a minute where reading German prose takes an evening and the family +has one reader for it. It appears only where a page carries it, in the value of an attribute +whose name ends in `-de` and in the few other places a page's own agent file names, and nowhere +else: not in git, not in a reply. A third language is not planned; adding one would mean +writing this paragraph again with its own reason. + +## How a text is made + +A text starts from a brief that names the audience, the one point, the facts it may claim and +where each is shown, and the file and place it lands. A draft without a brief is a draft the +reviewer has to reverse-engineer, and the reviewer is the person whose time is shortest. + +The writer of `WRITER.md` drafts the English on the branch, in the register the place calls +for, and reports what it wrote and which claims it could not trace to the brief or the +repository. The owner reviews on the branch, in the diff and on the rendered page, because that +is the review a pull request gets anyway and a second channel would be a second place to lose a +correction. + +The translator of `TRANSLATOR.md` makes the German from the reviewed English only, one element +at a time, with `GLOSSARY.md` open, and hands back a back-translation beside each element. +German made from a draft is German that has to be made again. An English edit to an element +re-runs the translator on that element alone; nothing marks an element stale, so the rule is +kept by whoever edits the English. + +Both roles edit files and neither commits. `WORKING.md` says who does. ## What every register shares @@ -61,6 +84,12 @@ a section carries the point; the rest is its support. Release notes are this register aimed at a consumer: what changed for them, what breaks and how to take it, in that order. +A page is this register aimed at a visitor who has not decided to stay. The first line is the +one point, in the words the visitor would use for it, and every later screen earns its place or +goes. Sentences run shorter than in a README because a page is read on a phone. A claim the +page opens with is a claim the page then shows. Nothing else changes: cause before mechanism +holds wherever a page explains, and no adjective sells. + ## The git register For commit messages and pull request descriptions. diff --git a/conventions/conventions-sync b/conventions/conventions-sync index 6e05229..7edcf4c 100755 --- a/conventions/conventions-sync +++ b/conventions/conventions-sync @@ -18,7 +18,7 @@ case $cmd in sync|check) ;; *) usage ;; esac PIN=conventions.json DIR=conventions -FILES="WRITING.md WORKING.md REPOSITORIES.md AGENTS.md conventions-sync conventions-check" +FILES="WRITING.md WORKING.md REPOSITORIES.md WRITER.md TRANSLATOR.md GLOSSARY.md AGENTS.md conventions-sync conventions-check" OPEN_RE='^$' CLOSE='' diff --git a/conventions/manifest.json b/conventions/manifest.json index 4869be1..8d00f46 100644 --- a/conventions/manifest.json +++ b/conventions/manifest.json @@ -1,12 +1,15 @@ { "repo": "robertblust/conventions", - "tag": "v1.4.0", + "tag": "v1.5.0", "files": { - "conventions/WRITING.md": "sha256:b490bc45be749d431aa32ecb9acc070111de95fe244764b4546da1eb8745296c", - "conventions/WORKING.md": "sha256:b78601d72afce4ecd01d9385452bb3e5422fbd1f3b2991efb7a56b069a1700f7", + "conventions/WRITING.md": "sha256:a8473e794f92eb5ebf973a22767fed951d226602394290c1ddcbb202f21d3ef1", + "conventions/WORKING.md": "sha256:ff96611192c07aaedd2071ac8dee2d2eacbc241db85b58beef2498bb966044a4", "conventions/REPOSITORIES.md": "sha256:b85d4a80144d46b432ee569eacee79644275f146d186c2cec2990b0a3e7be58f", - "conventions/AGENTS.md": "sha256:2cc3012c1834f103bebff6a075a7a19b05d59d1bbdba93b766d27e77c918cf9c", - "conventions/conventions-sync": "sha256:e062c0ed043ba9bd1e3246170b042c6e6665822dafd51c7ca90e19fd4ad6942a", + "conventions/WRITER.md": "sha256:7dc91881160145f114ec04911598fc464a55960cfa3eeb19ff7593e2c88eacd0", + "conventions/TRANSLATOR.md": "sha256:5b8298757917e102942440556676d6f242cd684caa24c22cf6f45b151b12c9a9", + "conventions/GLOSSARY.md": "sha256:704f4224caaa323dce576ba240005365a7e88e3a57060f430114deba1b496578", + "conventions/AGENTS.md": "sha256:91da7b8718aedf3868c01fb2ff7a6bfa305069bfdd65d546a9428a4e1e372388", + "conventions/conventions-sync": "sha256:4a59e85eee756399cf76a9f332d262694549b08472b46d45f26ac3314a28f4c2", "conventions/conventions-check": "sha256:af40f593bc49c90d6c306e90a8870f5874deab8bdd934a1bf1edcd5ec3efc6f9" } }