Monorepo RFC 008 l5 language conventions
RFC-008 — L5 Language Conventions: idiomatic rules inside each distribution form
| Field | Value |
|---|---|
| Status | *ccepted*(2026 |
| Author(s) | Rodrigo (with Claude as scribe) |
| Date | 2026 |
| Affects | The fifth and final taxonomic level — language and framework conventions inside each L4 distribution form |
| Depends on | monorepo-RFC-006-l4-distribution-forms.md |
1. Summary
Define *5*as the set of *diomatic conventions*of the canonical language and framework that each L4 distribution form uses. Unlike L1–L4, which are levels of directories, L5 is a level of rules: how files are laid out, how packages and modules are named, which build tools and lints apply, what test conventions are followed, and what platform-specific behaviors are expected.
L5 is the *ast taxonomic level* Below L5 is build tooling — go.mod, pubspec.yaml, Cargo.toml, package.json — which is governed by each language's own package manager, not by the monorepo taxonomy.
2. Why L5 is rules, not directories
Levels L1–L4 establish *here*things live (Domain → Area → Sector → distribution form). At L5, the question shifts: *given that we know we are inside app/mobile/, how should the contents be organized?*
The answer is dictated by the canonical language and framework chosen for that L4 form:
- Inside
app/mobile/, the canonical language is Dart (Flutter), and thelayout follows Flutter's
lib/,test/,assets/,pubspec.yamlconventions. - Inside
backend/, the canonical language is Go, and the layout followsGo module conventions (
cmd/,internal/,pkg/,go.mod). - Inside
app/cli/, the canonical language is Go with cobra, and the layoutfollows the cobra subcommand pattern.
Each canonical language brings its own set of conventions — and those conventions are L5.
3. The canonical-language mapping
| L4 form | Canonical language | Canonical framework | L5 reference |
|---|---|---|---|
backend/ |
Go | nethttp, gRPC, `sdkgo` | Go module layout (cmd/, internal/, pkg/, go.mod); table |
app/mobile/ |
Dart | Flutter | Flutter package (lib/, test/, pubspec.yaml, Material/Cupertino), koder_kit SDK |
app/desktop/ |
Dart | Flutter (desktop embedder) | Same as app/mobile/ with desktop form-factor adaptations |
app/tv/ |
TypeScript | React | TizenOS+WebOS app layout, D-pad navigation, tsconfig, eslint |
app/web/ |
Dual (see §6.2) | Flutter Web or Go templ + HTMX | Decision tree per use case |
app/cli/ |
Go | cobra | cobra command tree, --json flag, exit-code conventions |
app/tui/ |
Go (default) | Bubble Tea | Elm-style architecture (modelupdateview); Ratatui only as exception (see §6.3) |
engine/ |
Per-Sector | Per-Sector | L5 inside is the canonical layout of the engine's primary language; multi |
landing/ |
HTML/CSS + Go templ | koder_web_kit |
Landing |
and landing/` |
specs/errors/reporting.kmd |
All deployable L4 (backend, app |
and landing/` for branded products |
specs/readmes/products.kmd |
All Sectors in products/ |
5.3 Policies that govern L5 behavior
| Policy | Scope |
|---|---|
policies/sdk-first.kmd |
Forces every L4 to consult the SDK before reimplementing cross-cutting logic |
policies/language.kmd |
Source code in en-US; documentation/UI strings per audience |
policies/security.kmd |
Cross-cutting (HTTPS only, redirects, etc.) |
policies/regression-tests.kmd |
All L4 with executable code |
policies/hyperscale-first.kmd |
All L4 |
5.4 The implication
Adding a new L5 convention means writing a new spec under specs/ and referencing it here. This RFC is the *ndex* not the source of truth. The RFC must be updated whenever a spec is added, renamed, or retired.
6. Special cases
6.1 Multi-language engine/
Some engines expose the same artifact through multiple languages (C ABI, WASM, Go binding, Python binding, etc.). Inside such an engine/, L5 splits by language sub-tree:
engines/kodec/kodec/engine/
├── core/ ← canonical implementation (Rust or Kode)
├── c/ ← C ABI binding → C conventions apply inside
├── wasm/ ← WASM build → WASM conventions
├── go/ ← Go binding → Go conventions
├── python/ ← Python binding → Python conventions (pyproject.toml, src/)
└── rust/ ← Rust binding → Rust conventions (Cargo.toml, src/)Each sub-tree applies the L5 of its own language. The whole engine/ folder is one L4 form, but its internal L5 is multilingual by design.
This is the only case where L5 is visibly hierarchical inside a single L4 folder. In all other L4 forms, L5 is the single canonical language's flat layout.
6.2 Web-app duality: Flutter Web vs Go templ + HTMX
app/web/ is the only L4 form with *wo canonical paths* because the two have genuinely distinct use cases:
| Path | Use case | When to choose |
|---|---|---|
| *lutter Web*(Dart) | Rich |
When the web app is the same product as app/mobile/ and app/desktop/, and code reuse with the Flutter UIs is wanted |
| *o templ + HTMX* | Server |
When the page is mostly server-driven, SEO matters, or no Flutter Web counterpart exists |
*ule of thumb:*if the Sector already has app/mobile/ or app/desktop/ in Flutter and the web counterpart is the same product, use Flutter Web. If the web app is a stand-alone admin or content surface, use templ + HTMX.
The duality is *ocumented* not eliminated. Each Sector chooses one path for its app/web/ and notes the choice in koder.toml:
[app.web]
stack = "flutter" # or "templ-htmx"6.3 TUI canonical language: Go Bubble Tea (default), Rust Ratatui (exception)
app/tui/ adopts Go + Bubble Tea as the canonical default, aligned with backend/ and app/cli/. Rust + Ratatui is allowed only when the containing Sector is itself Rustnative (e.g., a Rustbased engine that ships a Rust TUI as a thin frontend over its API).
# Default — no declaration needed:
# implies Go + Bubble Tea
# Exception (must be justified):
[app.tui]
stack = "ratatui"
reason = "Sector is Rust-native; TUI shares types with engine/."The bias toward Go is intentional: Bubble Tea, the SDK ecosystem, and the backend stack share a language, which keeps cross-form code reuse high.
6.4 Internal vs external SDKs in engines/sdk/
Both internal SDKs (koder_kit, koder_ipc, koder_player, koder_web_kit) and external SDKs (go, js, python, dart, rust) live in engines/sdk/. Each Sector's engine/ follows the L5 of its target language:
| Sector | Canonical layout |
|---|---|
engines/sdk/go/engine/ |
Go module — pkg/, internal/, examples/ |
engines/sdk/python/engine/ |
Python package — pyproject.toml, src/, tests/ |
engines/sdk/dart/engine/ |
Dart package — lib/, test/, pubspec.yaml |
engines/sdk/rust/engine/ |
Rust crate — Cargo.toml, src/lib.rs |
engines/sdk/js/engine/ |
npm package — package.json, src/, tests/ |
engines/sdk/koder_kit/engine/ |
DartFlutter package — `lib, test/, pubspec.yaml` |
Each is treated as the canonical project of its language; nothing in this RFC overrides language-native conventions.
7. The lower bound — where the taxonomy stops
Below L5, the monorepo taxonomy yields control to each language's package manager and build tool:
- Go:
go.mod,go.sum,cmd/<binary>/main.go,internal/... - DartFlutter:
pubspec.yaml, `libfeature,testfeature_test.dart` - Rust:
Cargo.toml,src/lib.rs,src/bin/<binary>.rs - Python:
pyproject.toml,src/<package>/,tests/ - TypeScript:
package.json,tsconfig.json,src/,tests/
Files at this level are not part of the monorepo's normative taxonomy — they are governed by their language's ecosystem. The Stack does not override go.mod resolution, pub semver, or Cargo workspace rules.
8. How to add a new L5 convention
When a new convention needs to apply across one or more L4 forms:
- *rite a spec*in
meta/docs/stack/specs/<topic>/<rule>.kmdusing theKMD format. The spec must be normative (testable: pass/fail per file or per Sector).
- *eference the spec*in §5 of this RFC, marking which L4 form(s) it
applies to.
- *dd a CLAUDE.md trigger*if applicable, so contributors are reminded
to consult the spec before writing code in the relevant L4 form.
- *pdate tooling*(
/k-housekeep,/k-parity,/k-test,sdk-firstlint) to enforce or audit the new convention.
Conventions that fail steps 1–4 stay informal and are not part of L5.
9. Closing the taxonomy
With this RFC, the monorepo taxonomy is *omplete*
| Level | Term | Criterion | Document |
|---|---|---|---|
| L0 | repository root | — | (the monorepo itself) |
| L1 | Domain | form of consumption | RFC-003 |
| L2 | Area | purpose/theme within Domain | RFC |
| L3 | Sector | product/service identity | RFC-005 |
| L4 | distribution form | how it reaches the user | RFC-006 |
| *5* | *anguage conventions* | *diomatic rules of the canonical language* | *FC-008 (this RFC)* |
Below L5: build-tool territory, governed by each language's ecosystem.
The Stack now has one consistent criterion at every level, a closed vocabulary at L4, and an indexed convention layer at L5. Tooling, AI assistants, and human contributors share the same mental model when reasoning about where things go and how they are written.
10. Outstanding work
This RFC closes the taxonomic design. It does not execute the migration — moving the current root layout into the L1–L4 structure is a separate operational sweep, tracked outside the RFC stack. The migration plan must:
- Execute the path renames in RFC
003 §8 (with the RFC007 erratum:suite/→products/horizontal/). - Execute the L4 renames in RFC-006 §4 (
site/→landing/) and §5(
meta/sites/restructure). - Execute the L4 rename in the RFC-006 update (
lib/→engine/,inside
engines/Sectors only). - Execute the L4 brand
folder rename codified by RFC002 (engine/→backend/inside product/service Sectors).
Each rename is mechanical and can be batched into a single migration commit per Domain to keep blast radius small.