| 1 | # Contributing to codewhale |
| 2 | |
| 3 | Thank you for your interest in contributing to codewhale! This document provides guidelines and instructions for contributing. |
| 4 | |
| 5 | ## Getting Started |
| 6 | |
| 7 | ### Prerequisites |
| 8 | |
| 9 | - Rust 1.88 or later (edition 2024) |
| 10 | - Cargo package manager |
| 11 | - Git |
| 12 | |
| 13 | ### Setting Up Development Environment |
| 14 | |
| 15 | 1. Fork and clone the repository: |
| 16 | ```bash |
| 17 | git clone https://github.com/YOUR_USERNAME/CodeWhale.git |
| 18 | cd CodeWhale |
| 19 | ``` |
| 20 | |
| 21 | 2. Build the project: |
| 22 | ```bash |
| 23 | cargo build |
| 24 | ``` |
| 25 | |
| 26 | 3. Run tests: |
| 27 | ```bash |
| 28 | cargo test --workspace --all-features |
| 29 | ``` |
| 30 | |
| 31 | 4. Run with development settings: |
| 32 | ```bash |
| 33 | cargo run --bin codewhale |
| 34 | ``` |
| 35 | |
| 36 | ## Development Workflow |
| 37 | |
| 38 | ### Code Style |
| 39 | |
| 40 | - Run `cargo fmt` before committing to ensure consistent formatting |
| 41 | - Run `cargo clippy` and address all warnings |
| 42 | - Follow Rust naming conventions (snake_case for functions/variables, CamelCase for types) |
| 43 | - Add documentation comments for public APIs |
| 44 | |
| 45 | ### Testing |
| 46 | |
| 47 | - Write tests for new functionality |
| 48 | - Ensure all existing tests pass: `cargo test --workspace --all-features` |
| 49 | - Colocate unit tests beside the code they cover (standard Rust `#[cfg(test)]` |
| 50 | modules), and add integration tests under the owning crate's `tests/` |
| 51 | directory (for example `crates/tui/tests/` or `crates/state/tests/`). The |
| 52 | repository root `tests/` directory is not used |
| 53 | |
| 54 | ### Pre-push verification |
| 55 | |
| 56 | Run these before every push. They match what CI enforces on pull |
| 57 | requests, so passing locally means the PR lanes should pass too: |
| 58 | |
| 59 | ```bash |
| 60 | cargo fmt --all -- --check |
| 61 | cargo clippy --workspace --all-features --locked -- \ |
| 62 | -D warnings \ |
| 63 | -A clippy::uninlined_format_args \ |
| 64 | -A clippy::too_many_arguments \ |
| 65 | -A clippy::unnecessary_map_or \ |
| 66 | -A clippy::collapsible_if \ |
| 67 | -A clippy::assertions_on_constants |
| 68 | cargo test --workspace --all-features --locked |
| 69 | ``` |
| 70 | |
| 71 | The release lane runs a stricter clippy that also lints test, bench, and |
| 72 | example targets. The PR template checklist asks for this form, and it is |
| 73 | the right command before requesting review or doing release-bound work, |
| 74 | because `--all-features` alone skips lints that will fail the release |
| 75 | lane later: |
| 76 | |
| 77 | ```bash |
| 78 | cargo clippy --workspace --all-targets --all-features --locked -- \ |
| 79 | -D warnings \ |
| 80 | -A clippy::uninlined_format_args \ |
| 81 | -A clippy::too_many_arguments \ |
| 82 | -A clippy::unnecessary_map_or \ |
| 83 | -A clippy::collapsible_if \ |
| 84 | -A clippy::assertions_on_constants |
| 85 | ``` |
| 86 | |
| 87 | Some suites are slow, platform-bound, or intentionally excluded from the |
| 88 | default run; treat them as documented isolation cases rather than |
| 89 | failures of the normal gate: |
| 90 | |
| 91 | - **PTY snapshots** (`cargo test -p codewhale-tui --test qa_pty |
| 92 | --locked`) are Unix-only and internally serialized. One recovery-boot |
| 93 | case is `#[ignore]`d for a documented input-starvation issue. When a |
| 94 | PTY case fails, rerun that exact case in isolation and diagnose the |
| 95 | rendered frame before calling it a flake; `run_verifiers_background_*` |
| 96 | is the one known full-suite-parallelism flake that passes in |
| 97 | isolation. |
| 98 | - **Release runtime QA** (`cargo test -p codewhale-tui --test |
| 99 | release_runtime_qa --locked`) includes an `#[ignore]`d 32-worker storm |
| 100 | benchmark that is only run explicitly for evidence gathering. |
| 101 | - **OCR** (`image_ocr`) uses the macOS Vision framework or a locally |
| 102 | installed `tesseract`; its platform-specific paths are |
| 103 | `cfg(target_os = "macos")`-gated and depend on host tooling. |
| 104 | - **Seatbelt sandbox** tests are macOS-only (`cfg(target_os = |
| 105 | "macos")` at the module level) and do not run elsewhere. |
| 106 | |
| 107 | #### Local git hooks are optional |
| 108 | |
| 109 | This repository does not install git hooks, and no hook installer |
| 110 | exists; CI is the enforced gate. If you want a local `pre-push` hook |
| 111 | that runs the commands above, add it yourself (`.git/hooks/pre-push` or |
| 112 | `git config core.hooksPath`). Constraints for any local hook: |
| 113 | |
| 114 | - A hook must never push, tag, publish, deploy, mutate credentials, or |
| 115 | rewrite the working tree (no auto-fix commits or silent file |
| 116 | modification). It may only verify and report. |
| 117 | - To bypass your own hook for a knowingly documented reason (for |
| 118 | example, pushing work-in-progress to your own fork branch), use |
| 119 | `git push --no-verify` and say so in the PR description. Bypassing a |
| 120 | local hook does not make the gates pass — CI still runs them, and a |
| 121 | bypassed gate must never be reported as a passing one. |
| 122 | - Release publication (tags, GitHub Releases, crates/npm artifacts) is a |
| 123 | separate, owner-approved gate. Neither local hooks nor a green local |
| 124 | run authorize any publication step. |
| 125 | |
| 126 | ### Commit Messages |
| 127 | |
| 128 | Use clear, descriptive commit messages following conventional commits: |
| 129 | |
| 130 | - `feat:` New feature |
| 131 | - `fix:` Bug fix |
| 132 | - `docs:` Documentation changes |
| 133 | - `refactor:` Code refactoring |
| 134 | - `test:` Adding or updating tests |
| 135 | - `chore:` Maintenance tasks |
| 136 | |
| 137 | Example: `feat: add doctor subcommand for system diagnostics` |
| 138 | |
| 139 | When a commit harvests code from a community PR (see "How Your Contribution |
| 140 | Lands" below), include a `Harvested from PR #N by @author` line in the commit |
| 141 | body. An auto-close workflow watches for this pattern and closes the |
| 142 | referenced PR with credit so the contributor gets a clear signal that |
| 143 | their work shipped. |
| 144 | |
| 145 | ## How Your Contribution Lands |
| 146 | |
| 147 | We follow a deliberate "land what's useful, credit the contributor" model |
| 148 | that occasionally surprises new contributors. Two paths: |
| 149 | |
| 150 | ### Path 1 — Direct merge |
| 151 | |
| 152 | If your PR is well-scoped, passes CI, doesn't touch the trust-boundary |
| 153 | surface (auth / sandbox / publishing / branding), and doesn't conflict |
| 154 | with main, a maintainer merges it directly. This is the most common |
| 155 | outcome for small bug fixes and well-tested feature additions. |
| 156 | |
| 157 | ### Path 2 — Harvest |
| 158 | |
| 159 | If your PR is large, mixes scope, conflicts with main, or needs polish |
| 160 | that's faster for the maintainer to apply than to round-trip with the |
| 161 | contributor, the maintainer may **harvest** the useful commits or hunks |
| 162 | into a new commit on `main` rather than merging the PR directly. This is |
| 163 | **not a rejection** — it means your code landed. |
| 164 | |
| 165 | When this happens: |
| 166 | |
| 167 | - The harvested commit's message includes `Harvested from PR #N by |
| 168 | @your-handle`. This is the contract: that line is your credit and the |
| 169 | signal that your contribution shipped. |
| 170 | - If the maintainer copies or adapts your code, the harvested commit also |
| 171 | keeps attribution with the original author identity when possible: either by |
| 172 | preserving the commit author on a cherry-pick or by adding a |
| 173 | `Co-authored-by: Name <id+login@users.noreply.github.com>` trailer. This is |
| 174 | what lets GitHub's contribution surfaces recognize more than prose credit. |
| 175 | Maintainers should use `.github/AUTHOR_MAP`, or run |
| 176 | `gh api users/<login> --jq '"\(.id)+\(.login)@users.noreply.github.com"'`, |
| 177 | rather than copying raw, `.local`, or old-style noreply emails from a |
| 178 | contributor's machine. |
| 179 | - The `CHANGELOG.md` entry for the next release credits you by handle. |
| 180 | - The auto-close workflow closes your PR with a templated thank-you and |
| 181 | a link to the commit on `main`. |
| 182 | |
| 183 | When a maintainer closes a harvested PR by hand, the closing comment |
| 184 | follows this template (the pattern set on PR #2634): |
| 185 | |
| 186 | ```text |
| 187 | Closing with harvest credit, @handle — <what landed> landed via |
| 188 | <commit sha(s) or PR #N>. <If work remains:> The remainder is tracked |
| 189 | in #NNN — follow-ups welcome there. |
| 190 | Thank you for <one specific thing the contribution got right>. |
| 191 | ``` |
| 192 | |
| 193 | Three required elements: the contributor's handle, the exact commits or |
| 194 | PRs where their work landed, and — when the PR contained more than what |
| 195 | landed — a tracking issue for the remainder. A harvested PR is never |
| 196 | closed with a bare "superseded". |
| 197 | |
| 198 | To make a future contribution land via the faster Direct-Merge path |
| 199 | instead of the Harvest path, the highest-leverage things you can do are: |
| 200 | |
| 201 | 1. **Keep PRs single-purpose.** One bug fix per PR; one feature per PR. |
| 202 | Don't mix a refactor with a feature. |
| 203 | 2. **Rebase onto current `main` before opening the PR**, and after CI |
| 204 | feedback. Conflicts force the harvest path even when the change is |
| 205 | small. |
| 206 | 3. **Include tests** with new behavior. The maintainer often harvests |
| 207 | PRs without tests because adding the test is faster than asking the |
| 208 | contributor for one. |
| 209 | 4. **Avoid the trust-boundary surface** without prior maintainer |
| 210 | sign-off. That includes auth/credential flows, sandbox policy, |
| 211 | publishing/release plumbing, and `prompts/` content. PRs that touch |
| 212 | these without prior discussion are unlikely to merge directly even |
| 213 | when the change is well-implemented. |
| 214 | |
| 215 | ## Layered and EPIC-Sized Work |
| 216 | |
| 217 | Some architecture work is too large for one PR but still needs to be built in |
| 218 | dependent layers. For those changes, use this workflow: |
| 219 | |
| 220 | 1. Start with a tracking issue or EPIC when the work spans multiple PRs. Name |
| 221 | the intended slices and state what each slice is not trying to close yet. |
| 222 | 2. Keep each implementation PR focused on one behavior boundary. |
| 223 | 3. Later layers may stay in your fork or open as draft PRs while the lower |
| 224 | layer is still moving. Draft stacked PR titles or descriptions should say |
| 225 | `Draft / depends on #NNNN`. |
| 226 | 4. A dependent PR is not ready for merge review until the lower layer has |
| 227 | landed, the branch has been rebased onto current `main`, and the PR targets |
| 228 | `main`. |
| 229 | 5. The PR body should identify which earlier PR it builds on, what is in scope, |
| 230 | what is explicitly out of scope, which issues it references, and which local |
| 231 | commands were run. |
| 232 | 6. Use `Closes #...` only when the slice fully satisfies an issue. Use |
| 233 | `Refs #...` with a short `(partial)` note when the PR advances a broad issue |
| 234 | but leaves follow-up work. |
| 235 | 7. Structured commits are fine during review. Maintainers may squash or harvest |
| 236 | at merge time, with contributor credit preserved through authorship, |
| 237 | co-author trailers, changelog entries, or PR/issue comments. When the merge |
| 238 | commit itself carries a `Harvested from PR #N by @author` line, that PR is |
| 239 | merged with rebase or a merge commit rather than squashed, so the line |
| 240 | reaches `main` intact and the auto-close credit fires. |
| 241 | |
| 242 | Before asking for merge review on a layered PR, check that it is: |
| 243 | |
| 244 | - rebased onto current `main` |
| 245 | - marked ready for review, not draft |
| 246 | - focused to one behavior boundary |
| 247 | - backed by local command evidence in the PR body |
| 248 | - green in CI, or has any remaining red lane clearly explained |
| 249 | - covered by round-trip or migration-preservation tests when it changes config |
| 250 | or schema behavior |
| 251 | - referencing broad issues as partial unless it really closes them |
| 252 | |
| 253 | For layered work, a useful PR description shape is: |
| 254 | |
| 255 | ```text |
| 256 | Summary: |
| 257 | Scope: |
| 258 | Not in this slice: |
| 259 | Builds on: |
| 260 | Issues: |
| 261 | Validation: |
| 262 | ``` |
| 263 | |
| 264 | ## The Stewardship Branch |
| 265 | |
| 266 | Large refactors and architecture work stage on |
| 267 | `codex/v0.9.0-stewardship` before reaching `main`. The branch exists so |
| 268 | that multi-layer series (like the command-group refactor) can land layer |
| 269 | by layer against a stable base, get validated by their parity harnesses, |
| 270 | and then flow to `main` in periodic stewardship merges — instead of each |
| 271 | layer racing `main`'s daily churn. |
| 272 | |
| 273 | What this means for you: |
| 274 | |
| 275 | - **Base layered/EPIC-sized refactor PRs on `codex/v0.9.0-stewardship`** |
| 276 | and target the PR there (see #2888 for the model). Ordinary bug fixes |
| 277 | and features still target `main`. |
| 278 | - Maintainers merge the stewardship branch into `main` periodically; |
| 279 | your work reaches `main` with its history and credit intact. |
| 280 | - If you're unsure which base to use, ask in your tracking issue — the |
| 281 | default for anything that isn't a multi-PR series is `main`. |
| 282 | |
| 283 | ## Contribution Gate |
| 284 | |
| 285 | Codewhale uses a maintainer-managed contribution gate for the community front |
| 286 | door. Maintainers and collaborators bypass this gate automatically. The gate |
| 287 | workflows default to dry-run / comment-only mode so maintainers can observe the |
| 288 | signal before changing contributor flow. |
| 289 | |
| 290 | The maintainer posture is documented in |
| 291 | [docs/AGENT_ETHOS.md](docs/AGENT_ETHOS.md): automation should reduce load while |
| 292 | keeping good-faith contributors seen, credited, and able to keep helping. |
| 293 | |
| 294 | Issues are never auto-closed by the contribution gate. Unapproved external |
| 295 | issues receive a short welcome note that asks for reproduction details and then |
| 296 | remain open for maintainer triage. Codewhale depends on real edge cases from |
| 297 | real users, so issue intake should stay warm and open. |
| 298 | |
| 299 | Pull requests are different because they can touch code, CI, release plumbing, |
| 300 | auth, sandboxing, provider policy, and other trust-boundary surfaces. The PR |
| 301 | gate can be switched from dry-run to enforcement when maintainers decide they |
| 302 | need that safety control, but it should be treated as a review-load control, |
| 303 | not a judgment on contributor quality. Before enabling PR enforcement, seed the |
| 304 | allowlist broadly enough for active external contributors who should not be |
| 305 | interrupted by the rollout. |
| 306 | |
| 307 | The allowlist is scoped: |
| 308 | |
| 309 | - `pr:username` allows pull requests. |
| 310 | - `issue:username` allows issues. |
| 311 | - `all:username` allows both. |
| 312 | |
| 313 | A maintainer can approve someone by commenting `/lgtm` on a pull request for PR |
| 314 | access, or `/lgtmi` on an issue for issue access. The exact bare commands |
| 315 | `lgtm` and `lgtmi` are also accepted for compatibility, but the prefixed forms |
| 316 | are preferred because they are harder to trigger accidentally in ordinary review |
| 317 | discussion. |
| 318 | |
| 319 | Approvals do not edit `main` directly. The approval workflow opens a small |
| 320 | allowlist update PR so the new entry is reviewable before it takes effect. |
| 321 | |
| 322 | If the PR gate fires on a good contributor incorrectly, use the same approval |
| 323 | flow to restore them: comment `/lgtm`, merge the generated allowlist PR, then |
| 324 | reopen the affected pull request. If GitHub will not allow the closed PR to be |
| 325 | reopened, ask the contributor to resubmit after the allowlist PR is merged. |
| 326 | |
| 327 | ## Agent-Assisted Improvements |
| 328 | |
| 329 | Codewhale is allowed to help improve Codewhale, but the contribution still has |
| 330 | to be shaped for human review. The recommended workflow is the |
| 331 | [recursive self-improvement prompt](docs/RECURSIVE_SELF_IMPROVEMENT.md): run it |
| 332 | from a fresh fork or branch, let the agent find exactly one small friction point, |
| 333 | and stop after one patch. DeepSeek V4 Pro is the reference path for this loop |
| 334 | today, but any configured provider works — the review shape matters more than |
| 335 | the provider. |
| 336 | |
| 337 | Agents and maintainers should follow the stewardship posture in |
| 338 | [docs/AGENT_ETHOS.md](docs/AGENT_ETHOS.md): use automation for evidence, |
| 339 | verification, and narrow patches while keeping the final community decision |
| 340 | human-reviewed. |
| 341 | |
| 342 | The useful output is not "ideas for improvement." The useful output is a |
| 343 | specific reproduction, a minimal diff, focused checks, and a PR description that |
| 344 | explains the trade-off. Do not use an agent to touch auth, credentials, sandbox |
| 345 | policy, publishing/release plumbing, provider policy, telemetry, sponsorship, |
| 346 | branding, or global prompts without prior maintainer sign-off. |
| 347 | |
| 348 | ## Project Structure |
| 349 | |
| 350 | codewhale is a Cargo workspace. The live runtime and the majority of TUI, |
| 351 | engine, and tool code currently live in `crates/tui/src/`. Smaller workspace |
| 352 | crates provide shared abstractions that are being extracted incrementally. |
| 353 | |
| 354 | ``` |
| 355 | crates/ |
| 356 | ├── tui/ codewhale-tui binary (interactive TUI + runtime API) |
| 357 | ├── cli/ codewhale binary (dispatcher facade) |
| 358 | ├── app-server/ HTTP/SSE + JSON-RPC transport |
| 359 | ├── core/ Agent loop / session / turn management |
| 360 | ├── protocol/ Request/response framing |
| 361 | ├── config/ Config loading, profiles, env precedence |
| 362 | ├── state/ SQLite thread/session persistence |
| 363 | ├── tools/ Typed tool specs and lifecycle |
| 364 | ├── mcp/ MCP client + stdio server |
| 365 | ├── hooks/ Lifecycle hooks (stdout/jsonl/webhook) |
| 366 | ├── execpolicy/ Approval/sandbox policy engine |
| 367 | ├── agent/ Model/provider registry |
| 368 | ``` |
| 369 | |
| 370 | See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the live data flow across |
| 371 | these crates, including the bottom-up build order. |
| 372 | |
| 373 | ## Submitting Changes |
| 374 | |
| 375 | 1. Create a feature branch from `main`: |
| 376 | ```bash |
| 377 | git checkout -b feat/your-feature |
| 378 | ``` |
| 379 | |
| 380 | 2. Make your changes and commit them |
| 381 | |
| 382 | 3. Run the pre-push verification commands (see |
| 383 | [Pre-push verification](#pre-push-verification) above for the exact |
| 384 | gate and the stricter release clippy form) |
| 385 | |
| 386 | 4. Push your branch and create a Pull Request |
| 387 | |
| 388 | 5. Describe your changes clearly in the PR description |
| 389 | |
| 390 | ## Pull Request Guidelines |
| 391 | |
| 392 | - Use the [pull request template](.github/PULL_REQUEST_TEMPLATE.md) when opening |
| 393 | a PR — it includes the Summary, Testing, and Checklist sections reviewers |
| 394 | expect |
| 395 | - Keep PRs focused on a single change |
| 396 | - Update documentation if needed |
| 397 | - Add tests for new functionality |
| 398 | - Ensure CI passes before requesting review |
| 399 | |
| 400 | ## Shape of a Typical PR |
| 401 | |
| 402 | A well-structured PR follows a consistent pattern. Recent exemplars include: |
| 403 | |
| 404 | - **#386** — `/init` command: new `crates/tui/src/commands/groups/project/init.rs` module, project-type detection, |
| 405 | AGENTS.md generation, command registration in `commands/mod.rs`, localization strings. |
| 406 | - **#389** — Inline LSP diagnostics: LSP subsystem in `crates/tui/src/lsp/`, engine hooks in |
| 407 | `crates/tui/src/core/engine/lsp_hooks.rs`, config toggle, test coverage. |
| 408 | - **#387** — Self-update: new `crates/cli/src/update.rs` module, CLI subcommand registration, |
| 409 | HTTP download + SHA256 verification + atomic binary replacement. |
| 410 | - **#393** — `/share` session URL: new `crates/tui/src/commands/groups/project/share.rs`, HTML rendering, |
| 411 | `gh gist create` integration, command registration. |
| 412 | - **#343/#346** — (v0.8.5) Runtime thread/turn timeline and durable task manager refactors. |
| 413 | |
| 414 | Typically each PR touches 1–3 new files, modifies 2–5 existing files for wiring |
| 415 | (registries, dispatch matches, localization), and adds or updates tests. Changes |
| 416 | are scoped to a single feature or fix — if you discover related work that needs |
| 417 | doing, open a separate issue rather than expanding the PR scope. |
| 418 | |
| 419 | Before submitting, run the commands in |
| 420 | [Pre-push verification](#pre-push-verification). |
| 421 | |
| 422 | ## Reporting Issues |
| 423 | |
| 424 | When reporting issues, please use one of the issue templates: |
| 425 | |
| 426 | - [Bug report](.github/ISSUE_TEMPLATE/bug_report.md) — for reproducible problems |
| 427 | or regressions |
| 428 | - [Feature request](.github/ISSUE_TEMPLATE/feature_request.md) — for ideas and |
| 429 | improvements |
| 430 | |
| 431 | Issue reports should include: |
| 432 | |
| 433 | - Operating system and version |
| 434 | - Rust version (`rustc --version`) |
| 435 | - codewhale version (`codewhale --version`) |
| 436 | - Steps to reproduce the issue |
| 437 | - Expected vs actual behavior |
| 438 | - Relevant error messages or logs |
| 439 | |
| 440 | ## Security |
| 441 | |
| 442 | If you discover a security vulnerability, please do **not** open a public issue. |
| 443 | See [SECURITY.md](SECURITY.md) for the responsible disclosure process and |
| 444 | contact information. |
| 445 | |
| 446 | ## Code of Conduct |
| 447 | |
| 448 | Be respectful and inclusive. We welcome contributors of all backgrounds and |
| 449 | experience levels. See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for the full |
| 450 | code of conduct. |
| 451 | |
| 452 | ## License |
| 453 | |
| 454 | By contributing to codewhale, you agree that your contributions will be licensed under the MIT License. |
| 455 | |
| 456 | ## Questions? |
| 457 | |
| 458 | Feel free to open an issue for any questions about contributing. |
| 459 |