Component Naming Forms
naming specs/naming/forms.kmd
Forms of reference for every Koder Stack component: type, display name, bare name, slug, package, path, and aliases array. Every form derives from the bare name with no exception per type: display = "Koder <Bare>", slug = <bare-lowercase> (never prefixed — it is the repo, path and subdomain, namespaces that are already ours), package = koder-<slug> (the .deb/apt/Hub-catalog id, where the namespace is NOT ours and the prefix buys real disambiguation). Defines the regex per form and global uniqueness. §7 binds each rendering surface (landing title/hero/navbar, app title bar, Hub card, CLI, URLs, metric prefix) to a concrete form, closing the bare-vs-display ambiguity. Registry lives at meta/docs/stack/registries/stack-manifest.kmd and is CI-checked by `koder-spec-audit naming`.
When this spec applies
Primary triggers
- Referenciar ou nomear um componente Koder
All triggers
- Referenciar ou nomear um componente Koder em código, doc, conversa ou config
- Criar um componente novo (produto, serviço, engine, meta, umbrella)
- Adicionar ou renomear um alias coloquial (kflow, kdrafts, kterm…)
- Decidir o nome de binário CLI / D-Bus ID / Android applicationId
- Editar meta/docs/stack/registries/stack-manifest.kmd
Specification body
Spec: Component Naming Forms
Defines the forms of reference every component of the Koder Stack must
declare: a structural type, a prosaic display name, a contextual bare
name, a canonical slug, a package id, a filesystem path, and an optional
array of aliases for compact/colloquial reference.
Every form derives from the bare name, with no exception per type. There
is no rule that inspects the name and decides — display is always
Koder <Bare>, slug is always the lowercase bare, package is always
koder-<slug>. The one thing the forms distinguish is whose namespace they
land in: the slug names things that are already ours (the repo, the path, the
.koder.dev subdomain), so prefixing it only stutters; the package lands in
apt — shared with the world, where hub and talk are real Debian packages —
so it carries the prefix. That distinction is the whole spec; the rest is
regexes. See the rationale under R4.5 for the measurement that produced it.
Applies to every entry in meta/docs/stack/registries/stack-manifest.kmd.
1. The 7 forms
| Form | Example (Flow) | Example (Koda) | Example (Drafts) | Example (Kdb umbrella) | Where it's used |
|---|---|---|---|---|---|
| type | service | engine | meta | umbrella | classification |
| display | Koder Flow | Koder Koda | Koder Drafts | Koder Kdb | UI, marketing, prose docs |
| bare | Flow | Koda | Drafts | Kdb | conversation where Koder context is implied |
| slug | flow | koda | drafts | kdb | repo, path segment, subdomain |
| package | koder-flow | koder-koda | koder-drafts | koder-kdb | .deb/.kpkg Package:, apt, Hub catalog |
| path | services/foundation/flow | engines/lang/koda | meta/context/drafts | infra/data/kdb | filesystem reference |
| aliases | [kflow] | [] | [kdrafts] | [] | CLI binary, D-Bus tail, oral speech |
The "Where it's used" column is a summary. The exhaustive, normative surface→form mapping (which surface uses
displayvsbarevsslug) lives in §7.
2. Rules
R1 — type is required (enum)
type ∈ { product, service, engine, meta, umbrella }
product— user-facing app/CLI (products/**)service— API-consumed backend (services/**)engine— embeddable runtime/lib (engines/**)meta— reference material, not deployable (meta/**)umbrella— Sector grouping 2+ deployable components (e.g.infra/data/kdbgrouping kdb-kv/kdb-ts/kdb-vec). Has a name but no binary/CLI.
R2 — display is required and globally unique
- R2.1 —
display=Koder <Bare>, for everytype, with no exception (e.g.Koder Flow,Koder Koda,Koder Kdb,Koder Drafts). Owner, 2026-07-16: o mesmo motivo que matou as exceções da R4 — uma regra que decide por categoria produz superfícies que discordam.KdbeKoder Kdberam dois nomes para a mesma coisa, e nada dizia qual usar num título. - R2.3 — Title Case with single spaces. ASCII only.
R3 — bare is required
Equals display minus the Koder prefix (when present). Title Case.
R4 — slug is required, kebab-case lowercase, globally unique
- R4.1 —
slug=<bare-lowercase>, for everytype, with no exception (e.g.flow,hub,koda,kdb,drafts). Thekoder-prefix never appears in a slug. - R4.4 —
slugmatches^[a-z][a-z0-9]*(-[a-z0-9]+)*$. It must START with a letter (a slug is a DNS label and a path segment), and digits are legal anywhere after it.
Por que a R4.4 mudou junto (owner, 2026-07-16). A regex anterior era
^[a-z]+(-[a-z0-9]+)*$: dígito só era admitido depois de um hífen. Ela só aceitavakoder-bip39ekoder-s3porque o prefixo fornecia o primeiro segmento puro-letras e empurrava o dígito pro segundo. Com o prefixo fora do slug (R4.1),bip39es3— dois componentes reais — passariam a violar a própria spec. A regra velha não estava protegendo nada: nunca houve intenção de proibir dígito num nome, era efeito colateral de uma regex escrita quando todo slug era prefixado. A nova continua recusando o que importa (9foo,-foo,foo_bar, maiúscula).
- R4.5 — Proximity is not collision. Uniqueness (R2/R4/R6, enforced by T3–T5) is exact-match only. Phonetic or orthographic similarity between names is explicitly allowed and is not grounds for audit failure —
koda(engine),koru(product) andkora(product) may all coexist. The "distinctive" notes in the registry rationale column and inpolicies/naming-aliases.kmdare advisory branding guidance, never a hard gate.
Por que R4.1/R4.2/R4.3 morreram (owner, 2026-07-16). A regra decidia o prefixo pela inicial do bare — K isenta, não-K prefixa. Medido nos 186 componentes contra as superfícies vivas, o registry obedecia 186/186… e a realidade discordava dele:
superfície usa o slug? DNS (81 hosts) 0 de 81 — todos servem <bare>.koder.dev, nenhumkoder-x.koder.devexisteapt (22 pacotes) 14 koder-x, 3k<bare>, 5 bare — três formas para uma regraAPI do Hub (17) 15 slug, 2 k-form — e divergia do apt em khub≠koder-hub,kterm≠koder-termA inicial K era um proxy: nomes K aqui são inventados (não colidem), nomes não-K tendem a ser palavras comuns (colidem). O proxy correlaciona com o risco real — colisão em namespace compartilhado — mas não é ele, e erra nas pontas:
mediantnão começa com K, não colide com nada (sources.debian.org:exact: null), e carregava o prefixo mais caro da Stack (host inexistente, canonical quebrada). Enquanto issohubetalksão pacotes Debian reais — o risco existe, mas só onde o namespace não é nosso.Então o prefixo saiu do slug e foi para onde o risco vive: o
package(R7-bis). O.koder.deve o path do monorepo já são namespaces Koder — prefixá-los é gaguejar (koder-hub.koder.dev).Verificado antes de ratificar: com slug = bare, 0 colisões de unicidade (T4) entre os 186 — o prefixo não estava desambiguando nada dentro do registry.
R4-bis — package is required: the name in namespaces that are NOT ours
- R4b.1 —
package=koder-<slug>, for everytype, with no exception (e.g.koder-flow,koder-hub,koder-koda,koder-kdb,koder-kvs). This is thePackage:of the.deb/.kpkg, whatapt installresolves, and what the Hub's catalog API must serve under the same name it ships. - R4b.2 — A component that publishes no package still declares it. The name is derivable, and a field that only exists for publishers is a field nobody fills until the day it is needed.
- R4b.3 —
packagematches^koder-[a-z]+(-[a-z0-9]+)*$.
Por que
packageexiste como forma própria (owner, 2026-07-16). Oslugservia quatro senhores — "repo, path segment, package id, og:url host, subdomain" — e essas superfícies têm propriedades de colisão opostas:
- apt é compartilhado com o mundo:
hubetalkjá são pacotes Debian reais (hub= o CLI do GitHub, v2.14.2). Publicarhubcolidiria. O prefixo aqui compra desambiguação real..koder.deve o path do monorepo já são nossos:koder-hub.koder.devlê "koder hub koder dev". O prefixo aqui não compra nada e gagueja.Um campo não consegue estar certo nos dois. Antes desta separação a divergência não era nem declarável: o registry dizia
koder-hub, o apt serviakhub, e nenhuma linha da spec estava errada — porque não havia onde escrever a diferença. Medido:khub≠koder-hubekterm≠koder-termno ar, o catálogo 404ando o nome que o apt de fato instala.
koder-koda,koder-kvs,koder-kboxgaguejam o K, e é aceito. O owner ratificou a regra "sem exceções" sabendo disto e sabendo que reverte a direção do renamekoder-kruze→kruze(2026-07-12): a exceção custa mais que a gagueira. Uma regra com ramo por inicial foi o que produziu, em 186 componentes, três formas de nome no apt e um DNS que ignorava o slug 81 a 0.
R5 — path is required and must exist
- R5.1 — Absolute from monorepo root (e.g.
services/foundation/flow). - R5.2 — Path exists at audit time (CI verifies), unless the entry is
status: planned(R5.4). - R5.3 — Derived from the L1/L2/L3 taxonomy (
meta/docs/stack/taxonomy.md). - R5.4 —
status(optional) reserves a planned name. An entry MAY carry an optionalstatusform, expressed as an 8th registry column. Values:active(default; also the meaning of an absent/empty cell) — a shipping component;pathmust exist (R5.2 / T8).planned— a name reserved for a component that is part of a ratified architecture (e.g. an RFC-prescribed suite member) but not yet coded. Aplannedentry is exempt from T8 (itspathlegitimately does not exist yet) but still subject to every other rule (unique display/slug/aliases, prefix rules, regexes). When the component is built, drop thestatuscell (it becomesactive) and T8 then enforces the path. This prevents the anti-pattern of either (a) deleting a real, ratified name reservation just to pass an incomplete schema, or (b) registering a fake path. Sections without any planned member keep the 7-column layout; only a section that has one adds thestatuscolumn. Example:services/crypto/cipher(Krypt-suite member, krypt-RFC #060) reserved before code exists.
R6 — aliases is required (may be [])
- R6.1 — Array of strings, each globally unique across the entire registry (including cross-component, cross-type).
- R6.2 — Maximum 3 aliases per component.
- R6.3 — Each alias matches
^[a-z][a-z0-9]*$(compact only, no hyphens — alias is the compact form by design). - R6.4 — Each alias requires a 1-line
aliases_reasonin the registry entry. - R6.5 —
type = umbrelladefaults toaliases: []and never associates a binary/CLI. Aliases on umbrella entries are advisory only (for oral/written reference). - R6.6 — Aliases have no rigid prefix rule. Convention is
k<bare>compacted (kflow,kdrafts) when the bare name is a common English word, butflux,flowyetc. are valid if registered. Seepolicies/naming-aliases.kmdfor when to create an alias.
3. Test cases (run by koder-spec-audit naming)
| # | Check | Severity |
|---|---|---|
| T1 | Every registry entry has type, display, bare, slug, package, path. aliases field exists (may be []). | hard |
| T2 | type matches the enum (R1). | hard |
| T3 | display is unique across the registry. | hard |
| T4 | slug is unique across the registry. | hard |
| T5 | Each alias is unique across the registry (cross-component, cross-type). | hard |
| T6 | slug matches ^[a-z]+(-[a-z0-9]+)*$ (R4.4). | hard |
| T7 | Each alias matches ^[a-z][a-z0-9]*$ (R6.3). | hard |
| T8 | path exists in the monorepo (R5.2), unless status: planned (R5.4). | hard |
| T9 | display == Koder <Bare> for EVERY type — no per-type exception (R2.1). | hard |
| T10 | slug == <bare-lowercase> for EVERY type, and carries no koder- prefix (R4.1). | hard |
| T10b | package == koder-<slug> for EVERY entry (R4b.1, R4b.3). | hard |
| T11 | len(aliases) ≤ 3 (R6.2). Each alias has a non-empty aliases_reason (R6.4). | hard |
All severity is hard. Failure blocks PR merge via .gitea/workflows/audit-naming.yml.
4. Relationship to other specs
specs/binaries-and-cli/naming.kmdconsumes this spec. The binary name for a component isaliases[0]if present, elseslug. Updates §1, §3, §11 to reflect the new derivation.specs/naming/brand-score.kmdis orthogonal: it scores candidate names for branding fitness; this spec governs how an already-chosen name appears in its 6 forms.docs/stack/taxonomy.mddefines where the component lives (L1/L2/L3); this spec defines how to name it.docs/stack/vocabulary.mddefines the conceptual category (componente/documento/módulo/artefato/variante); this spec defines the surface forms.
5. Examples — full registry entries
product / bare-non-K
type: product
display: Koder Hub
bare: Hub
slug: koder-hub
path: products/dev/hub
aliases: [khub]
aliases_reason:
khub: "'hub' is a common English word; CLI binary already named khub"
engine / bare-K
type: engine
display: Koder Koda
bare: Koda
slug: koda
path: engines/lang/koda
aliases: []
aliases_reason: {}
meta
type: meta
display: Drafts
bare: Drafts
slug: drafts
path: meta/context/drafts
aliases: [kdrafts]
aliases_reason:
kdrafts: "'drafts' is a common English word; k-prefix signals meta-Koder context"
umbrella
type: umbrella
display: Kdb
bare: Kdb
slug: kdb
path: infra/data/kdb
aliases: []
aliases_reason: {}
note: "Groups kdb-kv, kdb-ts, kdb-vec, kdb-search, kdb-stream"
6. Migration
The current specs/binaries-and-cli/naming.kmd §1 rule (binary = k<slug>) is superseded by this spec. New derivation: binary = aliases[0] || slug. See specs/binaries-and-cli/naming.kmd for the updated derivation rules.
Existing components keep their current binary names. The registry's seed entries reflect the current state — no renames forced by this spec.
7. Form per surface — which form appears where
§1's "Where it's used" column is coarse. This section is the normative,
exhaustive mapping from rendering surface to form. It binds the surfaces that
the consuming specs left only structurally described ("nome do produto") to a
concrete form, closing the bare-vs-display ambiguity. Consuming specs
(landing-pages/products.kmd, landing-pages/packages.kmd,
desktop-apps/title-bar.kmd, koder-app/behaviors.kmd) reference this section
instead of re-stating the rule.
Surface → form
| Surface | Spec that renders it | Form | Example (Mediant) |
|---|---|---|---|
Landing <title>, OG/Twitter title + image:alt | landing-pages/products.kmd §Head | display | Koder Mediant — … |
Landing navbar .nav-brand text | landing-pages/products.kmd §Navbar | display | Koder Mediant |
Landing hero <h1> | landing-pages/products.kmd §Hero | bare (or distinctive brand form) — falls under R7.2: navbar + <title> anchor display on the same page | Meet Mediant / Mediant (also Koder Mediant) |
| Desktop window title bar | desktop-apps/title-bar.kmd; koder-app §7.3 | display | Koder Mediant |
Application-menu / launcher tile (.desktop Name=) | binaries-and-cli/naming.kmd §1.0 | bare — R7.2: a launcher grid is the densest repetition there is; every tile is a Koder tile, so Koder carries no information and pushes the distinguishing word off the label. The icon carries the attribution. | Mediant |
| In-app primary wordmark / brand header | koder-app/behaviors.kmd §7 | display | Koder Mediant |
Hub package-page heading + OG name (happy path) | landing-pages/packages.kmd | display (catalog name) | Koder Mediant |
| Hub OG fallback title (catalog data-integrity failure only) | landing-pages/packages.kmd §2.4 | slug — Koder Hub | mediant — Koder Hub |
Dense in-app chrome where display already on-screen: breadcrumb, tab label, compact secondary header, in-product nav item | per-app | bare | Mediant |
| Prose docs / conversation where Koder context is implied | — | bare | Mediant |
Repo, path segment, og:url host, subdomain | — | slug | mediant ⇒ mediant.koder.dev |
Package id (.deb/.kpkg Package:), apt, Hub catalog API | R4-bis | package | koder-mediant |
| Namespace de métrica (prefixo de exposição) | R7.6 | slug, -→_ | mediant_…, kdb_… |
| CLI binary, D-Bus name, deep-link scheme, IPC address | binaries-and-cli/naming.kmd; koder-app §8 | alias || slug | kmediant |
Rules
R7.1 —
displayis the identity-anchor form. The surfaces that anchor the product's identity — page<title>, OG/Twitter title, navbar/.nav-brand, desktop window title bar, in-app primary wordmark, Hub package-page heading / catalogname— must usedisplay(Koder <Bare>). These are the surfaces that establish which product this is; they are neverbare,slug, oralias.R7.2 —
bare/distinctive is for surfaces wheredisplayis already anchored on-screen. Once R7.1 putsdisplayon the page/screen, a secondary surface may usebare(<Bare>) — or a deliberate distinctive brand form — as expression. This covers (a) the landing hero<h1>, whose fleet convention is theMeet <Bare>template (e.g.Meet Jet,Meet Cron) or a distinctive lowercase brand (kicon,Kodix), since the navbar +<title>already anchordisplayon the same page; and (b) dense, repetitive in-app chrome where repeating "Koder" is noise — breadcrumbs, tab labels, compact secondary headers, in-product nav items; and (c) the application-menu / launcher tile, added 2026-07-12 — see below.displayitself remains valid on the surfaces in (a) and (b) (e.g. a hero readingKoder Mediantis fine) — R7.2 permitsbarethere, it does not forbiddisplay. The launcher tile is the exception:bareis required, per R7.5.R7.5 — the launcher tile is
bare, and it is derived from the REGISTRY. Searching "kode" in the GNOME menu used to surface five tiles readingKoder Kruze / Koder Term / Koder Dek / Koder Mosaic / Koder …— the word that distinguishes them came after the word that does not. In a launcher grid every tile is a Koder tile, soKodercarries no information and costs the label its first word; the icon is what carries the attribution.The tile takes the component's
bareform fromregistries/stack-manifest.kmd. It is NOTTitleCase(slug). That was the original mechanism (kicon#025) and it does not work: the slug carries thekoder-prefix or not for reasons that have nothing to do with this surface, soTitleCase(slug)yieldsKoder Termfor kterm,Koder Kruzefor kruze,Koder Iconfor kicon — andKodefor kode, whose slug happens to be bare. The five tiles the rule was written to fix would have stayed exactly as they were. A rule derived from the wrong field does not become right by being enforced.R7.3 —
slug/aliasesare never a human brand label.slugandaliasesnever appear as the visible name of the product in UI or marketing copy. Their surfaces are machine/address/CLI only (URLs, package ids, subdomains, binaries, IPC, deep links). The Hub OG fallback ({slug} — Koder Hub,packages.kmd §2.4) is the lone exception and fires only on a catalog data-integrity failure, never on the happy path.R7.4 — REVOGADA (owner, 2026-07-16). Dizia que meta/umbrella não levam o prefixo
Koder(R2.2), colapsandodisplay==bare. A R2.1 agora vale para todotypesem exceção:Koder Kdb,Koder Drafts. Não há colapso —displayebaresão formas distintas em toda entrada.R7.6 — o namespace de métrica é o
slug, com-virando_. Uma métrica exposta por um componente Koder começa por<slug com _>_: slugjet⇒jet_requests_total;kdb⇒kdb_rpc_total;mediant⇒mediant_…. A troca de-por_é mecânica — o formato de exposição do Prometheus não admite-em nome de métrica; nada mais do slug muda.Esta linha foi escrita duas vezes no mesmo dia, e a primeira estava certa (2026-07-16). Ao criar o
package, eu a reaponteislug→packagepor um raciocínio que soava bem: "um scraper Prometheus vê todos os alvos de uma vez, logo é namespace compartilhado, logo leva prefixo, como o apt". Errado, e a spec consumidora —observability/instrumentation-contract.kmd— já tinha a medição que derruba isso:- O prefixo compra desambiguação contra o que a gente REALMENTE raspa. O
Prometheus é self-hosted (
infra/observe/) e raspa componentes Koder mais exporters padrão (node_*,pg_*). Não existe umkdb_*de terceiro no alvo — o kdb+ da KX não é raspado por nós. O namespace é nosso, como o.koder.deve o path do monorepo. É o caso do DNS, não o do apt. - Proveniência num scrape se responde pelo label
job/target, nunca pelo prefixo do nome. Se o prefixo fosse a resposta,node_exporterseriaprometheus_node_*. - Custo medido de repontar:
kdb_*aparece 126× na árvore,kode_*30×. Todas virariamkoder_kdb_*/koder_kode_*— dashboard e alerta quebrados em massa, por zero ganho de desambiguação. - Já foi tentado e registrado como erro. O
instrumentation-contractdiziakoder_<service>_e exigiakoder_kdb_*de um componente cujo slug ékdb; o resultado foi um segundo sistema de identidade, paralelo ao registry, e 86 das 125 "violações" eram conformes o tempo todo. Repontar propackagereintroduziria exatamente esse erro — desta vez com a spec o abençoando.
A regra que decide continua a mesma: prefixo só onde o namespace não é nosso. O apt é o único que satisfaz isso hoje. Métrica não é.
Por que esta linha existe. A §7 se diz exaustiva e nunca ligou esta superfície a forma nenhuma — então o
instrumentation-contract.kmdfez o que o preâmbulo desta seção manda não fazer: re-enunciou a regra por conta própria. Ligar a superfície aoslugnão afrouxa nada: aperta, porque o prefixo deixa de ser string livre e passa a ser um slug registrado, sujeito a T1–T11.- O prefixo compra desambiguação contra o que a gente REALMENTE raspa. O
Prometheus é self-hosted (
References
meta/docs/stack/specs/binaries-and-cli/naming.kmdmeta/docs/stack/specs/naming/brand-score.kmdmeta/docs/stack/policies/naming-aliases.kmdmeta/docs/stack/registries/stack-manifest.kmdmeta/docs/stack/taxonomy.mdmeta/docs/stack/vocabulary.md