Thanks for the interest. This project lives or dies by the trust in its verdicts, so contributions need to keep the determinism contract intact.
By submitting a pull request, issue, patch, or any other contribution to this repository, you certify that:
- You wrote the contribution, or you have the right to submit it under the MIT License.
- Your contribution is licensed to the project under the MIT License.
- You grant Cüneyt Öztürk (the project author and copyright holder) a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license to use, reproduce, modify, distribute, and sublicense your contribution under any terms, including future commercial or dual-license arrangements.
This is a standard developer-certificate-of-origin-plus-re-licensing clause used by projects like Grafana, MongoDB (pre-SSPL), and Sentry. It keeps the open-source commitment intact (nothing can be removed from MIT) while allowing the project to sustain itself through enterprise licensing if that becomes necessary.
See NOTICE and docs/COMMERCIAL.md for the trademark and commercial-use policy.
Contributors using Claude Code should also read CLAUDE.md — it encodes the repo's prime directive and development rules.
By participating, you agree to uphold the Code of Conduct. See CODE_OF_CONDUCT.md.
- If you've never used falsify before, do TUTORIAL.md first — it's the fastest way to internalize the lock-then-run contract before you start changing the code.
- Every new feature or bug fix must include unittest coverage.
- Never break the exit-code contract (
0PASS,10FAIL,2bad spec,3hash mismatch,11guard violation). If you need a new code, propose it in an issue first. - Canonical YAML hashing is the spine — do not change the canonicalization rules without an RFC-style issue.
- Stdlib +
pyyamlonly. No new dependencies without discussion.
-
Fork and clone.
-
Install the one dependency:
make install(orpip install -e .for an editable install that also exposes thefalsifyconsole entry point). -
Run the full local CI suite:
make ci(unittest + smoke + calibration end-to-end + skill lint). -
All of those must pass before you open a PR.
-
(Recommended) Install pre-commit hooks so style + guard checks fire locally on every commit:
pip install pre-commit pre-commit install pre-commit install --hook-type commit-msgConfiguration lives in
.pre-commit-config.yaml; see also docs/PRE_COMMIT.md.
- Branch naming:
feature/<short-slug>orfix/<short-slug>. - Commit messages: present-tense verb first, subject line under 72 chars. Explain the why in the body.
- If Claude (or any LLM) co-authored the commit, include a
Co-Authored-By:trailer. - One logical change per commit — easier to revert, easier to review.
- Tests added and passing (
python3 -m unittest discover tests -v) - Smoke test passing (
bash tests/smoke_test.sh) - If you changed
falsify.pysemantics, updateDEMO.mdanddocs/ARCHITECTURE.md - If you added a CLI flag, update
README.md - No new dependencies beyond
pyyaml(unless discussed in an issue first)
- Add
cmd_<name>(args)infalsify.py. - Wire it into the argparse setup alongside the existing subcommands.
- Add
tests/test_<name>.pyusingunittest+tempfile.TemporaryDirectoryfor isolation — every test should run in a throwaway.falsify/directory. - Add a one-liner to the commands list in
README.md. - If the subcommand is juror-demo-relevant, add a mention to
DEMO.md.
- Skills live in
.claude/skills/<name>/SKILL.mdwith YAML frontmatter whose keys arename,description,allowed-tools,context. - Subagents live in
.claude/agents/<name>.mdwith YAML frontmatter whose keys arename,description,tools,model,context. - All skills and subagents must use
context: forkunless there's a justified reason to share parent context. - Add a
tests/test_skill_<name>.pyortests/test_agent_<name>.pythat validates the frontmatter parses as YAML and contains the required keys. - The CI workflow's
skill-lintjob will re-validate everySKILL.mdand agent file on push.
Open an issue with:
- The exact
falsifycommand that failed. - Exit code you got.
- Exit code you expected.
- Minimal
spec.yamlthat reproduces it. - Output of
python3 falsify.py --versionso we know which version you were on.
If you find a way to make falsify guard pass on a contradicting
claim — or to make two semantically different specs produce the
same canonical hash — open a private security advisory or email
the maintainer directly. Do not file a public issue for these;
they break the core guarantee and deserve a coordinated fix.
- Python: type hints on public functions, a docstring on anything more than 5 lines of body.
- YAML: 2-space indent, lowercase keys, double-quoted strings when the value contains punctuation that YAML plain scalars would mishandle.
- Markdown: reference-style links where the target is reused, inline links when it's one-off.
-
Update
__version__infalsify.pyto the new version (andversioninpyproject.tomlto match —tests/test_pyproject.pyenforces this). -
Update
CHANGELOG.md— move items from[Unreleased]into a new## [X.Y.Z] — YYYY-MM-DDsection with today's date. -
Before pushing a tag, run:
make release-checkExpected output ends with
Ready to tag and push.If any gate FAILs, fix before tagging. WARN gates (typically placeholder scans on<USER>/<VIDEO_URL>, or a dirty working tree) require human judgment — read the message and decide whether to proceed. -
Commit the version bump, then tag and push:
git tag vX.Y.Z git push origin main --tags -
.github/workflows/release.ymlfires on the tag push. It runs the unittest suite + smoke test, verifies the tag matches the code's__version__, builds sdist + wheel, and creates the GitHub Release with the matching CHANGELOG section as the body. -
Verify the Release page on GitHub once the workflow finishes. If the workflow failed, fix forward — delete the tag locally and remotely, fix the issue, re-tag, re-push.
By contributing, you agree that your work is released under the MIT license of this repository.