Contributing to Clients
Below are the core principles for building public facing APIs exposed in our clients:
- APIs should be lovable
- Provide sane defaults but make them flexible, extensible and overridable
- Consistency is king
- It should be easy to do things right, hard to do things wrong
- Never expose more than is needed
Shared documentation snippets
Section titled “Shared documentation snippets”Chronicle’s shared public documentation is language agnostic. It uses unified language tabs for examples that apply across clients. The shared Chronicle page owns the explanation and where the tab group appears, but each client repo owns its own code snippet.
This keeps the published docs readable while keeping client-specific APIs close to the code that can verify them.
Ownership
Section titled “Ownership”Place snippets in Documentation/client-snippets/** in the repo that owns the client:
- C#:
Chronicle/Documentation/client-snippets - Kotlin:
Chronicle.Kotlin/Documentation/client-snippets - Java:
Chronicle.Kotlin/Documentation/client-snippets-java - Elixir:
Chronicle.Elixir/Documentation/client-snippets - TypeScript:
Chronicle.TypeScript/Documentation/client-snippets
Snippet files can be .md or .mdx. Prefer .md for ordinary fenced code snippets; use .mdx only when the snippet file itself needs MDX syntax.
Do not create a public client-specific Chronicle section just to hold equivalent snippets. The snippet folders are source-only and are expanded into the shared pages by the documentation site sync.
Do not add new raw csharp, cs, java, kotlin, elixir, typescript, or ts fenced examples to shared Chronicle docs for client SDK behavior. Use <ChronicleClientTabs /> instead. The Documentation repo runs npm run chronicle-client-docs:check, which fails on missing snippets and on any increase in direct client-language fences in shared Chronicle docs.
Add a shared example
Section titled “Add a shared example”Shared Chronicle pages reference those snippets with a placeholder:
<ChronicleClientTabs snippet="events/appending/example" />The snippet id is extensionless. Add the same id under each participating client repo:
Chronicle/Documentation/client-snippets/events/appending/example.mdChronicle.Kotlin/Documentation/client-snippets-java/events/appending/example.mdChronicle.Kotlin/Documentation/client-snippets/events/appending/example.mdChronicle.Elixir/Documentation/client-snippets/events/appending/example.mdChronicle.TypeScript/Documentation/client-snippets/events/appending/example.mdUse one fenced code block in each snippet file unless the language needs more than one block. The fenced language must match the owning client, for example csharp, java, kotlin, elixir, or typescript.
<ChronicleClientTabs /> has no clients property. Chronicle’s shared docs never declare which clients apply to an example — the sync discovers that itself: it checks every registered client’s snippet root for a file at that id and renders a tab only for the clients where one exists. When only some clients expose a concept, simply don’t add a snippet file for the others; there is nothing to configure on the placeholder.
When a shared workflow should be visible for a client but that client does not support it yet, keep the tab visible with an explicit unsupported snippet instead of omitting the client:
```textThis Chronicle client does not support this workflow yet.Track the client SDK issue before using this API from this language.```The default synchronized tab key is chronicle-client, so choosing TypeScript in one tab group selects TypeScript in the other tab groups on that page. Use a custom syncKey only when tab groups should not follow each other.
Validation and CI
Section titled “Validation and CI”Each client repo must also keep Documentation/validate-client-snippets.py wired into its Client Snippet Verification workflow. That workflow compiles the snippets against the client source when either Documentation/client-snippets/** or Source/** changes, so a client API change cannot silently leave the shared Chronicle docs stale.
Run the validator in each repo whose snippets changed:
python3 Documentation/validate-client-snippets.pyThen run the documentation site build from the Documentation repo:
cd webnpm run buildExternal client repos also need a documentation sync workflow that triggers the build-docs workflow in the Cratis/Documentation repo after snippet changes land on main.
Client-specific pages
Section titled “Client-specific pages”Use snippets for examples that belong inside shared Chronicle pages. Use client-specific pages only when the content is genuinely different for that client, such as installation, connection setup, runtime integration, language-specific APIs, framework integration, decorators or annotations, package behavior, generated APIs, or troubleshooting.
Chronicle concepts and feature workflows belong in the shared Chronicle docs. Do not add client-specific concept or guide trees for events, reactors, reducers, read models, projections, constraints, seeding, transactions, migrations, or compliance when the same explanation applies across clients.
Client-specific pages still live in the owning client repo’s Documentation/** folder:
- .NET pages render under
/chronicle/clients/dotnet/fromChronicle/Documentation/clients/dotnet/** - Kotlin pages render under
/chronicle/clients/kotlin/ - Elixir pages render under
/chronicle/clients/elixir/ - TypeScript pages render under
/chronicle/clients/typescript/
The documentation site imports those pages into the Chronicle navigation under Client SDKs. The Documentation/client-snippets/** folders are skipped and never become public pages.
If a client-specific page later becomes generally useful across clients, move the explanation into the shared Chronicle docs and use <ChronicleClientTabs /> for the code.
npm run chronicle-client-docs:check audits public client docs for shared-topic overlap using Documentation/web/chronicle-client-docs.yml. That count is a ratchet: it can go down as duplicated client pages are consolidated into shared docs, but it must not increase.
If an old client URL must remain as a pointer to the shared docs, mark it as a bridge page:
---sharedTopicBridge: true---Bridge pages should only explain where the canonical shared topic lives and where to find client-specific setup or reference details.
Add another Chronicle client
Section titled “Add another Chronicle client”To add another client language later:
- Add the client repo to the Documentation repo checkout/submodule setup.
- Add it to
Documentation/web/chronicle-client-docs.yml. - Add
Documentation/client-snippets/**to the client repo when it participates in shared tabs. A new client only shows up in a tab group once it has a matching snippet file — no other wiring makes it opt-in or required. - Add
Documentation/validate-client-snippets.pythat compiles snippets against that client. - Add a
Client Snippet Verificationworkflow in the client repo. - Add the documentation sync workflow that triggers the Documentation repo after
Documentation/**changes land onmain. - Add the new language’s snippet files for the shared Chronicle pages that should include it.
- Run the affected snippet validators,
npm run chronicle-client-docs:check, andnpm run buildinDocumentation/web.
Migration audit
Section titled “Migration audit”The current docs have migrated shared client examples behind <ChronicleClientTabs />. The Documentation repo keeps a direct-fence baseline in web/scripts/chronicle-client-docs-baseline.json; the normal audit allows direct-fence counts to decrease but fails if they increase.
The aggregate client-doc check also reports public client pages that still look like shared Chronicle topic pages. Treat those counts as migration debt: move the shared explanation into Chronicle docs, replace code with synchronized tabs, then lower the manifest baseline.
Use strict mode to see the remaining migration work:
cd Documentation/webnpm run audit:chronicle-client-docs:strictWhen migrating a page, replace each client SDK example with <ChronicleClientTabs snippet="..." />, add matching snippets in the participating client repos, run each affected client snippet validator, then refresh the baseline after the direct-fence count decreases.
Topics
Section titled “Topics”| Topic | Description |
|---|---|
| Client Types | The different client types and relationships between them. |
| .NET | The .NET Client. |
| API | The REST API Client. |
| Internalization | The process of internalization. |