# Contributing to Ktav
**Languages:** **English** · [Русский](CONTRIBUTING.ru.md) · [简体中文](CONTRIBUTING.zh.md)
Thanks for looking. Ktav is a small format on purpose — the bar for
accepting changes is deliberately high, and that bar is **not** "would
this be a nice feature." The bar is: **does this change make Ktav
smaller, simpler, or more coherent?**
This document covers (1) what belongs in the spec, (2) what doesn't,
(3) how the version/release process works, and (4) how to submit.
## What belongs here
This repository is the format itself. Three kinds of change land here:
### 1. Editorial — `PATCH` bump
Fixes that do not change what a conforming parser does. Typos,
reworded paragraphs, clearer examples, better test names, new
*conformance* tests (new angles on existing rules — not new rules).
Ship a PR directly. One-line description in the commit, updated
version in `versions/<v>/spec.md` header and `CHANGELOG.md`.
### 2. Additive — `MINOR` bump
Backward-compatible extensions. A new keyword, a new primitive form,
a new multi-line-string variant — something old parsers would reject
but whose absence doesn't break existing documents.
Open an issue first. The issue must answer, in order:
1. **What does this let users do that they currently can't?** Show a
real config, not a synthetic example.
2. **What's the cost?** Every rule added is a rule every implementer
has to get right, and a rule every reader has to know.
3. **Can the same thing be expressed within the existing grammar?**
If yes, that's usually the better answer.
Additions ship as a new `versions/<x>.(y+1)/` directory with its own
spec and tests. The previous version stays frozen — implementations
continue to pin to whichever version they support.
### 3. Breaking — `MAJOR` bump
A change in grammar or semantics that makes some previously-valid
document invalid, or changes its meaning. These are rare. Same
process as additive, plus a migration note that describes what
changes and why.
Breaking changes land in a new `versions/(x+1).0/` directory.
Previous majors remain published forever — implementations targeting
them are not obsoleted.
## What doesn't belong here
- **Parser tricks** — if it's not expressible in the spec document,
it's an implementation concern. File it in the relevant
implementation's repo (`ktav-lang/rust`, etc.).
- **Performance claims** — also implementation-level.
- **Integrations** (how Ktav talks to your favourite DI framework,
schema validator, etc.) — downstream of the spec.
- **"Why not just use YAML / TOML / JSON"** threads — the README's
comparison table is the whole answer we maintain.
## Design principles
Every proposal is weighed against these, in priority order:
1. **Locality.** A line's meaning must not depend on a declaration
elsewhere in the file, or in another file.
2. **One sentence.** The new rule must be statable in one sentence
of the specification. If it takes a paragraph, it's probably
actually two rules.
3. **No whitespace sensitivity** (other than line breaks). Ktav is
line-based; column alignment never carries meaning.
4. **No magic types.** Strings are strings; numbers are strings at
the Value level. Typing is the consumer's job (via serde, via
schema, via code).
5. **Explicit over clever.** `::` is verbose on purpose — the rare
case where you need a literal should be the one that costs
extra characters, not the common case.
A proposal that sacrifices any of these needs a correspondingly
large justification.
## Versioning, concretely
```
x . y . z
│ │ └── PATCH — editorial; parsers unaffected
│ └────── MINOR — additive; old docs still valid
└────────── MAJOR — breaking
```
- `versions/<x>.<y>/` is the source of truth for that version.
- `CHANGELOG.md` at the repo root tracks history across all versions.
- `versions.ktav` is the machine-readable index.
- Each release is marked with a git tag carrying the **full**
`MAJOR.MINOR.PATCH`: `v0.1.0`, `v0.1.1`, `v0.2.0`, … The directory
name (`versions/0.1/`) drops the PATCH component by convention — a
PATCH bump updates the directory in place, not next to it — but the
version string everywhere else stays three-part.
## Submitting
1. Fork, branch, commit with a descriptive message (English or
Russian both accepted).
2. If the change touches `versions/<v>/`, update the header version
and add a `CHANGELOG.md` entry under the right heading.
3. Add a conformance test in `versions/<v>/tests/` — either under
`valid/` (new shape parses correctly) or `invalid/` (new
constraint rejects the right input). One test per concept.
4. Open a PR referencing the issue (for additive/breaking changes).
Editorial PRs don't need a prior issue.
5. CI in downstream implementations will check that its parser still
agrees with the updated test suite.
## Review
Maintainers look for:
- Does the change pass the five principles above?
- Is there a test that would fail if someone implemented it wrong?
- Is the one-sentence rule actually one sentence?
- Does the example read well out of context?
Small, surgical PRs merge fast. Large "while I'm here" refactors of
the spec text get pushed back — break them up.
## Language policy
The `ktav-lang` organisation maintains every prose document in three
parallel languages: English (canonical), Russian, and Simplified
Chinese. File naming: `<name>.md` / `<name>.ru.md` / `<name>.zh.md`.
When you change any prose file, **update all three versions in the
same commit** — translation drift is the main failure mode of
multi-language docs. If you don't speak one of them, mark the
untouched files with `<!-- TODO: sync with <name>.md -->` at the top
and open the PR anyway; a maintainer or community contributor will
fill the gap before merge.
`versions/<v>/spec.md` is normatively English; RU / ZH translations
are informative (helpful to non-English readers, not a source of
authority). RFC 2119 keywords (MUST, SHOULD, MAY, …) stay in English
capitals across every translation — they are technical terms.
Full policy in the org-level
[`.github/AGENTS.md`](https://github.com/ktav-lang/.github/blob/main/AGENTS.md).
## License
By contributing, you agree that your contribution is licensed under
the MIT License, same as the rest of this repository.