Monorepo RFC 008 l5 language conventions

RFC-008 — L5 Language Conventions: idiomatic rules inside each distribution form

Field Value
Status *ccepted*(20260429)
Author(s) Rodrigo (with Claude as scribe)
Date 20260429
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 the

    layout follows Flutter's lib/, test/, assets/, pubspec.yaml conventions.

  • Inside backend/, the canonical language is Go, and the layout follows

    Go module conventions (cmd/, internal/, pkg/, go.mod).

  • Inside app/cli/, the canonical language is Go with cobra, and the layout

    follows 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); tabledriven tests; gofmt/golangcilint
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; multilanguage engines split L5 by language subtree (see §6.1)
landing/ HTML/CSS + Go templ koder_web_kit Landingpage spec (`specs/landingpages

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) Richclient SaaS app — login, dashboards, realtime data, complex forms, offline-capable PWAs 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* Serverrendered surfaces — admin consoles, internal dashboards, content sites with interactivity, fastloading pages 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:

  1. *rite a spec*in meta/docs/stack/specs/<topic>/<rule>.kmd using the

    KMD format. The spec must be normative (testable: pass/fail per file or per Sector).

  2. *eference the spec*in §5 of this RFC, marking which L4 form(s) it

    applies to.

  3. *dd a CLAUDE.md trigger*if applicable, so contributors are reminded

    to consult the spec before writing code in the relevant L4 form.

  4. *pdate tooling*(/k-housekeep, /k-parity, /k-test, sdk-first

    lint) 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 RFC004 (+ RFC007 nomenclature)
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:

  1. Execute the path renames in RFC003 §8 (with the RFC007 erratum:

    suite/products/horizontal/).

  2. Execute the L4 renames in RFC-006 §4 (site/landing/) and §5

    (meta/sites/ restructure).

  3. Execute the L4 rename in the RFC-006 update (lib/engine/,

    inside engines/ Sectors only).

  4. Execute the L4 brandfolder 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.

Source: ../home/koder/dev/koder/meta/docs/stack/rfcs/monorepo-RFC-008-l5-language-conventions.md