# Contributing to Athanor
Thanks for taking the time to contribute. Athanor is a young library —
PRs, bug reports, and design feedback all welcome.
## Development setup
Prerequisites: Elixir `~> 1.17` and Erlang/OTP `~> 26`. The CI matrix
runs against Elixir 1.17 / 1.18 / 1.19 on OTP 26 / 27.
```bash
git clone https://github.com/Arsenalist/athanor.git
cd athanor
mix deps.get
mix test
```
Format before pushing:
```bash
mix format
```
Build docs locally to preview:
```bash
mix docs && open doc/index.html
```
## Branching & PRs
- Branch off `main`. Name branches anything sensible — `fix/`, `feat/`,
`docs/` prefixes appreciated but not enforced.
- Keep PRs focused: one logical change per PR. Big rewrites get
reviewed faster when split.
- Every PR needs a `CHANGELOG.md` entry under `## [Unreleased]`. Match
the section style of prior entries (`### Added` / `### Changed` /
`### Fixed` / `### Removed`).
- Every PR runs the CI matrix. PRs that don't pass `mix test` or
`mix format --check-formatted` won't be merged.
## Tests
- Library code (under `lib/athanor/`) needs corresponding tests under
`test/athanor/`. `lib/athanor/foo.ex` → `test/athanor/foo_test.exs`.
- Tests are `use ExUnit.Case, async: true` by default. Anything that
touches `Application.get_env/put_env` must reset state in `on_exit`.
- The architecture suite (`test/athanor/tree_architecture_test.exs`)
enforces the host-agnostic boundary: no `AmplifyWeb.*`, no
`Amplify.*`, no `Ecto.*`, no `Jason.*` references in `lib/athanor/`.
Consumer-specific bindings go behind runtime config — see
`Athanor.Components.Text` for the canonical pattern.
## Semantic versioning policy
While in `0.x`:
- **Minor bump (0.x → 0.x+1)** — any public API change, including
breaking changes, new behaviour callbacks, new required fields on
existing structs. Document under `### Changed` or `### Removed`.
- **Patch bump (0.x.y → 0.x.y+1)** — bug fixes, doc improvements,
internal refactors that don't change public behavior.
Once we hit `1.0`, we switch to strict SemVer (major bumps for breaking
changes).
## Public API contract
Anything documented in `@moduledoc` or with `@doc` is part of the
public contract. Anything else is implementation detail and may change
between patch releases.
Modules under `Athanor.Internal.*` (none today, but reserved) are
explicitly private.
## Boundary rules
Athanor is host-agnostic by design — it must work in any Phoenix app,
not just the one that gave it life. Don't add:
- Runtime dependencies on host applications (Amplify, anyone else's
app code).
- Ecto / database concerns. Components accept and emit maps.
- JSON encoding/decoding — the caller supplies maps, the caller
encodes for storage. `Jason` is dev-test only.
- Gettext / locale-specific helpers. The host app handles i18n.
The `lib/athanor/components/` tree may grow with primitive components
(text, heading, button, divider, columns, image). Heavier or
host-specific components (rich-text editors, asset pickers, product
selectors) stay in consumer apps and integrate via the
`Athanor.Component` behaviour + `:custom` field type.
## Releases
Releases happen from `main` only:
1. Update version in `mix.exs`.
2. Promote `## [Unreleased]` to `## [x.y.z] - YYYY-MM-DD` in
`CHANGELOG.md`.
3. Commit + tag (`git tag vX.Y.Z && git push --tags`).
4. `mix hex.publish` from the tagged commit.
## Reporting bugs
Open an issue with:
- Elixir / OTP / `phoenix_live_view` versions.
- Minimal reproduction.
- Expected vs. actual.
Security issues: email instead of opening a public issue — address
listed on the maintainer's GitHub profile.
## License
By contributing, you agree your changes are licensed under the
[MIT License](LICENSE) that covers the rest of the project.