design-RFC-006 — Cross-language token distribution

draft

Decides how koder_kit (Flutter / Dart) and koder_web_kit (JS) and future native consumers (Kotlin Android, Swift iOS, Rust embedded) receive the canonical Verge tokens. Evaluates copy-on-build vs publish-and-import. Recommendation: copy-on-build via CI workflow for v0; promote to a published koder_design_tokens package per language when consumer count crosses 3.

designRFC006 — Cross-language token distribution

*tatus* v0.1.0 — Draft. Decides distribution mechanics for the perlanguage token files designgen already emits (#059).

R1 — Problem

tools/design-gen emits per-language token files under dist/tokens/:

  • tokens.css / tokens.scss — for web stylesheets
  • tokens.json — DTCGaligned, machinereadable
  • tokens.dart — Flutter consumers
  • tokens.kt — Android / Kotlin Multiplatform
  • tokens.swift — iOS / macOS native
  • tokens.d.ts — TypeScript types

These files are emitted to dist/ at build time but the consumers (engines/sdk/koder_kit, engines/sdk/koder_web_kit, future native SDKs) currently re-declare tokens by hand. Structural drift between spec and implementation.

The token export side is solved (design-gen #059); the distribution side — how do consumers receive the export? — is open.

R2 — Options evaluated

Option A — Copyonbuild (CI workflow copies into each consumer)

Workflow runs after design-gen ships; for each registered consumer, copy the relevant file into the consumer's repo / subdir, open a PR (or push directly to a branch the consumer auto-merges from).

*ros*

  • No publication step needed (no pub.dev / npm / Maven Central

    account-management).

  • Atomic with design-gen release (token bump propagates same hour).
  • 100% selfhosted; aligns with `policies/selfhosted-first.kmd`.
  • Drift impossible — consumer's copy IS the source export.

*ons*

  • Consumers can't pick which token version to consume (always latest).
  • Each consumer needs a matching CI workflow on their side.
  • Doesn't scale gracefully past 5–6 consumers.

Option B — Publishandimport (publish per-language package)

Publish a koder_design_tokens package per language ecosystem:

  • pub.dev (Dart)
  • npm (TypeScript / JS)
  • Maven Central (Kotlin)
  • Swift Package Manager (Swift)

Consumers import as a regular dependency, bump version semantically.

*ros*

  • Consumers control version (pin to ^0.x for stability).
  • Scales to any consumer count (industry-standard import).
  • Discoverable by community.

*ons*

  • Each ecosystem needs publication credentials + automation

    (pub.dev token, npm token, Maven publishing pipeline, SPM repo).

  • Latency between code change and consumer adoption (publication +

    consumer bumps version).

  • Conflicts mildly with self-hosted-first.kmd G1 if the ecosystem

    registries go down (pub.dev / npm / Maven are external infra).

Option C — Hybrid: copyonbuild v0, publish at scale-out

Start with Option A (copyonbuild); promote to Option B per ecosystem once that ecosystem has ≥ 3 consumers (per policies/reuse-first.kmd SDK-extraction threshold).

*ros*

  • Defers publication overhead until justified by consumer count.
  • Avoids premature standardization.
  • Each ecosystem promotes independently when ready.

*ons*

  • Two distribution mechanisms coexist during transition periods.
  • Some bookkeeping of "which ecosystem is on which mode" needed.

R3 — Decision criteria

Gate A (Copyonbuild) B (Publish) C (Hybrid)
G1 — selfhostedfirst ⚠ external registries ✅ start; ⚠ promotes
G2 — reuse-first SDK ≥ 3 consumers gate starts at 0 starts at 0 (over-engineered) ✅ promotes when threshold met
G3 — latency atomic publication + consumer bump atomic v0
G4 — consumer control of version none (always latest) full starts none; gains when promoted
G5 — community discoverability none high gains when promoted

R4 — Recommendation

*dopt Option C (hybrid)*with the following thresholds:

Ecosystem v0 mode Promotion trigger
Dart (Flutter) copyonbuild into koder_kit ≥ 3 Flutter consumers ratified beyond koder_kit
TypeScript / JS copyonbuild into koder_web_kit ≥ 3 JS consumers beyond koderwebkit
Kotlin / Android copyonbuild into koder_kit Android side ≥ 3 Android consumers beyond koder_kit
Swift / iOS copyonbuild into koder_kit iOS side ≥ 3 iOS consumers beyond koder_kit
CSS / SCSS copyonbuild into koderwebkit ≥ 3 standalone CSS consumers

policies/reuse-first.kmd SDK-extraction threshold (≥ 3 consumers) serves as the natural promotion trigger.

R5 — Implementation tickets (post-ratification)

Subtickets in `tools/designgen (or a new tools/token-bridge`):

  1. tools/token-bridge#001 — CI workflow: on every design-gen tag,

    render token files for each ecosystem.

  2. tools/token-bridge#002koder_kit copy worker:

    dist/tokens/tokens.dartkoder_kit/lib/src/tokens/verge_tokens.dart + commit with tokens-sync: bump to design-gen vX.Y.Z.

  3. tools/token-bridge#003koder_web_kit copy worker:

    dist/tokens/tokens.css + tokens.d.tskoder_web_kit/src/tokens/ + commit.

  4. tools/token-bridge#004 — Drift check: per-PR CI in koder_kit /

    koderwebkit fails if local tokens differ from dist/tokens/ checksum.

When promotion threshold hits for an ecosystem, additional sub-ticket to set up the publication pipeline + migrate consumers.

R6 — Cross-references

  • Coordinates with tools/design-gen#059 — emission side is done.
  • Coordinates with projects/koder-stack#157 (Figma kit) — Figma

    reads tokens.json directly via the plugin; orthogonal to this RFC's per-language ecosystem question.

  • Aligns with policies/reuse-first.kmd ≥ 3 consumers SDK-extraction

    threshold.

R7 — Risk mitigation

  • *oken rename in Verge breaks consumer* copyonbuild forces the

    CI in each consumer to fail at the bump PR — consumer fixes before merge. No silent breakage.

  • *I complexity grows* keep the copy worker generic

    (tools/token-bridge) so adding a new consumer is configuration, not new code.

  • *ockstep releases* designgen tags drive consumer bumps;

    consumers MAY pin to an older version via revert if a token bump introduces a regression. (Promotedtopublish ecosystems get proper semver pinning out of the box.)

Não-escopo

  • Perlocale tokens (i18n) — token names are universal; localeaware

    formatting (numbers, dates) is a runtime concern, not a token-bundle concern.

  • Runtime token livereload — out of v0; token bumps go through

    the consumer's build + release cycle.

  • Theming over Verge (Material 3 preset overrides, custom presets) —

    handled by canonicalPresets in designgen / uistyle.kmd, not by this distribution RFC.

Source: ../home/koder/dev/koder/meta/docs/stack/rfcs/design-RFC-006-cross-language-token-distribution.kmd