Contributing¶
This is a solo, personal project — but if an agent (or future-you) is touching it, follow
the same rules the tooling enforces. These rules live in AGENTS.md and are summarized
here.
Branch policy (mandatory)¶
- Never commit directly to
main. -
Every change gets its own branch:
testing— integration and QA branch (auto-buildsspooty-testing.apk).feature/<slug>— new capability (auto-labeled asenhancement).fix/<slug>— bug fix (auto-labeled asbug).chore/<slug>— tooling, docs, CI.
-
Commit on the branch, push it, and open a pull request.
- PRs can be merged automatically via GitHub Actions if you assign the
automergelabel and CI is green.
Automerge & collaborators¶
The Auto Merge workflow (.github/workflows/automerge.yml) watches for the automerge
label and merges a PR once CI and branch-protection checks are green:
- It runs on
pull_request_target, so it also works for PRs from forks and from collaborators with push access to the repo. - By default it refuses to auto-merge external fork PRs (
MERGE_FORKS: denied): only in-repo branches (owner or collaborator) can auto-merge. A fork contribution still merges normally via a manual review + merge. - Approvals are not auto-granted (
MERGE_REQUIRED_APPROVALS: 0). If the repo grows and you want a mandatory human review before auto-merging, bumpMERGE_REQUIRED_APPROVALSto1and/or enable "Require a pull request before merging" branch protection onmain— the auto-merge action waits for those checks before squashing.
Rule of thumb for a shared repo: only slap
automergeon a PR after an actual human has reviewed it. Branch protection is the safety net that makes that enforceable.
The verification sweep¶
Before pushing any branch, run the full sweep locally and confirm every step passes.
We provide a Docker-based local development wrapper (./spooty.sh) that contains JDK 17 and the Android SDK to bypass local host incompatibilities. Always use this wrapper instead of ./gradlew:
If you touched the injected JS, also rebuild its TypeScript sources on the host (the Docker container has no Node) and commit the regenerated assets:
CI recompiles the assets from web/src/ on every run and fails if the committed *.js
files diverge — treat app/src/main/assets/*.js as generated files and never hand-edit them.
CI runs the same sweep on every PR/push. Never leave a red build behind — fix locally, push again.
Conventional Commits & Auto-Releases¶
We use Semantic Release to fully automate versioning, tags, and the changelog.
- All commits MUST follow Conventional Commits (e.g. feat: xyz, fix: abc).
- Do NOT manually edit CHANGELOG.md. It is regenerated automatically by the bot when your PR lands in main.
- Merging to main auto-tags the repo and generates the official spooty-vX.Y.Z.apk GitHub Release.
Style & sanity¶
- Run
./spooty.sh ktlintFormatand let the formatter decide — don't hand-format. - Dependency/plugin versions live only in
gradle/libs.versions.toml. - Keep pure logic in
inject/,settings/, and other non-framework code, and cover it with JVM unit tests. - Don't hand-edit generated files (
build/,app/src/main/assets/*.js,lint-baseline.xml,detekt-baseline.xml,local.properties, signing files). - Don't commit secrets, signing keys or
local.properties. - Load the relevant skill before touching those areas:
skills/ad-engine/,skills/theming/,skills/webview-security/,skills/optimization/.