# Contributing
## Prerequisites
| [Rust stable](https://rustup.rs/) | Build the daemon |
| [Trunk](https://trunkrs.dev/) | Build the Yew UI (`cargo install trunk`) |
| `wasm32-unknown-unknown` target | UI target (`rustup target add wasm32-unknown-unknown`) |
The `wasm32` target and Trunk are only needed when working on the browser UI
(`ui/`). The daemon itself is a native binary and builds without them.
## Setup
```sh
git clone https://github.com/moadim-io/daemon
cd daemon
cargo build
```
Run the checks the pre-push hook enforces before any push:
```sh
cargo fmt --check
cargo clippy
cargo test
```
Enable the bundled git hooks once per clone:
```sh
git config core.hooksPath .githooks
```
## Architecture at a glance
The daemon (`src/`) is an [Axum](https://github.com/tokio-rs/axum) server that
exposes the same cron-job functionality over three interfaces on one port:
- **REST** — handlers in `src/routes/http.rs`
- **MCP** — handlers in `src/routes/mcp.rs`
- **UI** — a separate Yew/WASM crate in `ui/`, embedded at build time
Jobs are persisted to the OS crontab so they run on schedule. See
[`Architecture.md`](Architecture.md) for the full picture.
## Tests
```sh
cargo test
```
Tests must live in `*_tests.rs` sibling files, **not** inline
`#[cfg(test)] mod foo { … }` blocks — the pre-push hook rejects inline blocks.
A colocated module reference is fine:
```rust
#[cfg(test)]
mod cron_jobs_tests; // semicolon, points at cron_jobs_tests.rs
```
The pre-push hook also requires 100% line coverage (excluding `main.rs`) via
[`cargo-llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov):
```sh
cargo install cargo-llvm-cov
rustup component add llvm-tools-preview
cargo llvm-cov --fail-under-lines 100 --ignore-filename-regex 'src/main\.rs'
```
## Workflow
1. Branch from `main` — name it `feat/...`, `fix/...`, `chore/...`, or `docs/...`.
2. Keep commits focused; one logical change per commit.
3. Open a PR against `main`; fill in what changed and why.
## Code conventions
- New REST routes go in `src/routes/http.rs`; register them in the router
builder there (the `.route(...)` chain). New MCP tools go in
`src/routes/mcp.rs`.
- Error variants belong in `src/error.rs` (`AppError`); fallible handlers
return `Result<_, AppError>`, which converts to the right HTTP status.
- No `unwrap()` in handler paths — propagate errors via `AppError`.
- `apis/openapi.json` and `schemas/job.schema.json` are generated at build time
— never edit them by hand.
## Adding a cron-job field
1. Add the field to the `CronJob` struct in `src/cron_jobs.rs`.
2. Add a matching `Option<T>` field to `UpdateRequest`.
3. Apply the update in the `update` handler and reflect the change in the
crontab sync.
4. Add a unit test in the `cron_jobs_tests.rs` sibling file.
Cron entries are persisted in the OS crontab — use `crontab -e` / `crontab -l`
to inspect state during development. The daemon must be able to invoke
`crontab` on the host.
## Commit messages
Conventional Commits: `type(scope): subject`.
```text
feat(cron): add pause/resume endpoint
fix(sync): handle missing crontab gracefully
docs: correct contributor setup steps
```
Types: `feat`, `fix`, `chore`, `refactor`, `test`, `docs`.