The conventions file
conventions.json lives in your repository root and is committed to git, like any other project rule. cix init writes a first version; you own it from there.
A complete example
{
"stack": { "language": "typescript", "framework": "vue" },
"folders": {
"components": "src/components",
"composables": "src/composables",
"utils": "src/lib",
"pages": "src/views"
},
"naming": {
"components": "PascalCase",
"composables": "camelCase",
"utils": "camelCase",
"pages": "PascalCase"
},
"language": { "code_identifiers": "en", "comments": "en" }
}The keys
folders
A map of kind to the directory files of that kind belong in, relative to the document. Checked when your assistant asks about a specific path and kind — a components file outside src/components comes back invalid, with the corrected path spelled out.
The longest declared folder wins, so src/components/forms can be its own kind alongside src/components.
naming
A map of kind to filename case. Three values are evaluated: PascalCase, camelCase, snake_case. The check inspects the filename stem — UserCard.vue is the stem UserCard — and returns a corrected name when it doesn't match.
Declare anything else and cix reports it as not assessed rather than passing it. That's deliberate: see Rules cix can't evaluate.
stack
Detected language and framework, written by cix init. Informational — nothing enforces it — and worth keeping accurate because it's the fastest description of the project for anything reading the document.
language
Which human language your identifiers, comments, and UI strings are written in. Recorded for readers, human and otherwise; no check evaluates it today. Useful in a codebase where the answer isn't obvious and half the identifiers ended up in a second language.
Kinds are yours
Nothing in cix defines a fixed list of kinds. A kind is any key you put in folders or naming, and rules apply only to the kinds you declare. Two consequences worth planning around:
- Undeclared kinds are unconstrained. No rule for
scriptsmeans a script can go anywhere without complaint. That's a valid choice, not a gap. - A kind declared in
namingbut notfoldersis checked for its name and not its location, and the reverse works too. Declare only the half you actually care about.
Writing one by hand
The defaults cix init writes cover Vue and Python projects; every other stack starts empty, with folders and naming as empty objects. That's the honest starting point — an empty document reads as "this project declares no rules" rather than as a missing file.
To fill it in:
- Describe what's true, not what you wish were true. A rule that half your existing files break turns every check into noise, and the audit into a wall of violations nobody reads.
- Start with the kinds that hurt when they drift — usually components, models, and tests. Two accurate rules beat eight aspirational ones.
- Point folders at the directory that actually holds them today. Run the audit afterwards; if it reports a pile of misplaced files, the rule is likely wrong about the project rather than the other way round.
- Commit it. It's a project rule, and it belongs in review like any other.
Note — after editing the document, the local naming gate picks up the change immediately, because it reads the file on disk. The on-demand check reads cix's stored copy, which is refreshed when your assistant next starts up in that repository, or when you re-run cix init.
Monorepos: nested documents
A subdirectory can carry its own conventions.json, and the nearest document above a file is the one that governs it. Paths inside a nested document are relative to that document, so apps/api/conventions.json declaring models: "src/models" governs apps/api/src/models.
When a nested document applies, cix says which one it used. Knowing whether an answer came from the root document or a package-level one is the difference between fixing the rule and fixing the file.
How it reaches cix
The file on disk is the source of truth. cix keeps a mirrored copy per repository so a lookup doesn't have to ship the whole document on every call, and that mirror is refreshed in two places:
| Moment | What happens |
|---|---|
cix init | Uploads the current document |
| Assistant startup in the repo | Compares disk against the stored copy and re-uploads if they differ |
Both fail quietly. Being offline, or not yet signed in, never stops cix init from finishing or a write from landing — the local naming gate keeps working from the file on disk regardless.
Next: Conventions in use.