Contributing¶
Thanks for your interest! This project is built in the open, test-first, one reviewed PR at a time. This guide explains how we work.
Ground rules¶
- Be kind. See the Code of Conduct.
- Every change lands via a pull request against
main, with CI green. - One logical change per PR. Small, reviewable PRs merge faster.
- Most work is tracked in issues. If you want to work on something, comment on the issue (or open one) first so we don't duplicate effort.
Test-Driven Development (required)¶
We develop RED → GREEN → REFACTOR:
- RED — write a failing test that captures the desired behaviour. Run it, watch it fail for the right reason.
- GREEN — write the minimum code to make it pass.
- REFACTOR — clean up with the tests as your safety net.
Don't open a PR whose production code isn't driven by tests.
For the rule engine, correctness is checked against real kotlinx-serialization with the round-trip oracle: serialize with the old model, decode with the new one, and assert the classifier predicted the actual outcome. A rule without an oracle-backed test is not done.
Development setup¶
Requires JDK 17+. Everything runs through the Gradle wrapper — no local Gradle needed.
./gradlew build # compile + test + spotlessCheck + apiCheck + docs gates + docs-sample tests
./gradlew test # tests only
./gradlew spotlessApply # auto-format (run before committing)
./gradlew koverHtmlReport # coverage report -> build/reports/kover
Docs site¶
The docs site (chrisjenx.github.io/serialkompat) is MkDocs Material; content lives in docs/. To preview locally:
python3 -m venv build/docs-venv && build/docs-venv/bin/pip install -r requirements-docs.txt
build/docs-venv/bin/mkdocs serve # live-reload at http://127.0.0.1:8000
The Docs workflow runs on any PR touching docs/**, docs-samples/**, mkdocs.yml, requirements-docs.txt, main.py (the version macro and redirect stubs), CHANGELOG.md, CONTRIBUTING.md, scripts/check_docs_site.py or .github/workflows/docs.yml. It runs dokkaGenerate, then mkdocs build --strict, then scripts/check_docs_site.py site (redirect stubs and anchors). Broken links, broken nav, a page missing from the nav, or a broken redirect fail the PR.
Where the docs come from, and the build gates that keep them in step with the code (all run by ./gradlew build):
docs-samples/holds the Kotlin models and thesnippets/*.gradle.ktsblocks the pages embed with--8<--.buildcompiles and tests it, andDocsSnippetsFunctionalTestapplies each snippet in a TestKit build (helponly, so it checks DSL names and types, not values such asacceptedBreaksstrings). Edit the sample, not a copy in the page.docs/snippets/reports/*are rendered by the real reporters inDocsReportSamplesTest(serialkompat-core), which fails if they drift. Regenerate them with./gradlew :serialkompat-core:test -Pserialkompat.updateDocs=true.checkDocsReferencefails if a DSL property or block, a task, a-Pserialkompat.*property or a CLI flag is undocumented. It wants the exact backticked name: DSL members indocs/configuration.md(nested ones as dotted paths, such ashistory.dirorreports.json.required), tasks as`serialkompatCheck`, and properties and flags as`-Pserialkompat.ref=<ref>`or`--format`indocs/tasks-cli.md.checkRulesDocfails if any rule the classifier ships (aRules.*constant) is missing fromdocs/rules.md.checkRulesProoffails if a rule has no### `RULE_ID`section under the page's Rule reference, if a section has no Proof line citing a test, or if a cited test isn't declared in the file its link points at. Wrap each test name in backticks in the link text. The allowed files are the JSON and CBOR round-trip oracles plus a short list of unit tests (proofTestPathsin the rootbuild.gradle.kts).
Before you push¶
Run the full local gate. CI runs the same build, plus the secret scan and (for docs changes) the strict docs build:
./gradlew spotlessApply && ./gradlew build
- Formatting is enforced by Spotless + ktlint.
spotlessApplyfixes most issues. - Public API stability is enforced by binary-compatibility-validator. If you intentionally change a module's public API, run
./gradlew apiDumpand commit the updated*.apifile. The diff is part of your PR review. RunapiDumpandapiCheckas separate Gradle invocations;./gradlew apiDump apiCheckfails with a validation error. - Docs are gated too (
checkRulesDoc,checkRulesProof,checkDocsReference; see Docs site). Add the rule's row and section when you add a rule, and document a new option, task or flag. serialkompat-annotationsis Kotlin Multiplatform with Apple targets, which only build on macOS. On Linux they are skipped silently, so a green Linux build doesn't prove that module; CI builds it on a separate macOS job.- Coverage is reported by Kover.
- Secrets are scanned by gitleaks. CI runs it over the full history (
Secret Scanworkflow). To catch a secret before it is ever committed, enable the pre-commit hook once:pip install pre-commit && pre-commit install. Never commit credentials or signing keys; publishing secrets live only in CI secrets. - The Gradle wrapper is upgraded only with
./gradlew wrapper --gradle-version <x>, never by editinggradle-wrapper.propertiesor swapping the jar. A partial bump leavesgradlew/gradlew.baton the old version. TheUpdate Gradle Wrapperworkflow opens wrapper PRs; Dependabot is configured to skip them.
Code style¶
- Kotlin official style (
.editorconfig+ ktlint, version pinned ingradle/libs.versions.toml). 4-space indent, 120 col. - Library modules (
-core,-extractor,-annotations) useexplicitApi(). Declare visibility and public return types explicitly. - Prefer small, single-purpose types with clear interfaces (see the design doc's isolation principles).
- Public declarations get KDoc.
Commit messages¶
Use clear, imperative subject lines (e.g. Add DISCRIMINATOR_COLLISION rule). Reference the issue: Fixes #12. Conventional-commit prefixes (feat:, fix:, docs:, test:, chore:) are welcome but not required.
Pull requests¶
- Fill in the PR template.
- Link the issue the PR closes.
- Ensure CI is green and the branch is up to date with
main. - Expect review focused on correctness, test quality, and adherence to the design.
Project layout¶
| Module | Responsibility |
|---|---|
serialkompat-core |
Pure-Kotlin model, differ, classifier, rules, report. No I/O. |
serialkompat-extractor |
Runtime SerialDescriptor extraction (JVM). |
serialkompat-gradle |
The Gradle plugin. |
serialkompat-cli |
Standalone serialkompat diff <baseline> <current> CLI. Not published; no tracked API. |
serialkompat-annotations |
@SerialkompatIgnore / @SerialkompatChecked discovery markers (Kotlin Multiplatform). |
The authoritative design lives in docs/design. If a change deviates from the design, say so in the PR and we'll update the design together.
Licensing of contributions¶
By contributing, you agree that your contributions are licensed under the Apache License 2.0, the same license as the project.