Files
Claude 59a6ffc940
CI / Build and check (push) Successful in 8m6s
CI / Deploy to Cloudflare Pages (push) Successful in 29s
CI / Build and check (pull_request) Successful in 8m13s
CI / Deploy to Cloudflare Pages (pull_request) Skipped
CLAUDE.md: local build, Gitea shipping flow and Katan rule sets
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016eTor2tpxKb4dXbd8RqnvZ
2026-10-17 18:31:29 +00:00

4.9 KiB
Raw Permalink Blame History

Working on this repo with Claude

CI runs on Gitea Actions at git.wallacenet.ca/devadmin/ProLibertateGames (workflows in .github/workflows/), publishing to Cloudflare Pages (plg.wallacenet.ca). The workflow below was measured on the dev machine (~/ProLibertateGames); it replaces the earlier GitHub-sandbox notes, which no longer apply.

Build, test and lint locally

Gradle works on the dev machine. Two environment variables are required: the default JDK is 25, which Gradle 8.14 cannot use, and the Android SDK lives in the home directory.

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export ANDROID_HOME=$HOME/android-sdk      # platform 34, build-tools 34.0.0
./gradlew testDebugUnitTest lintDebug assembleDebug

The Katan tests run in seconds; a clean build takes a few minutes. Run the full suite, lint and assembleDebug before pushing: they are what CI runs. There is no KVM, so no emulator; the engine and the UI logic are covered by unit tests (see docs/UI-catan.md).

Shipping: branch, PR, CI, merge

  1. Branch from dev; before pushing, merge origin/dev in (it moves between sessions).
  2. Plain git push has no credentials. Use the Gitea token as a one-off header: git -c "http.extraheader=Authorization: token $(cat ~/.config/gitea/token)" push -u origin <branch>.
  3. Open the PR with POST /api/v1/repos/devadmin/ProLibertateGames/pulls (same token, Authorization: token ...). CI (ci.yml) runs on the PR: build, unit tests and Android lint; it publishes nothing. Poll /actions/runs until it completes (about 7 minutes).
  4. Merge with POST .../pulls/N/merge {"Do":"merge"}. The push to dev runs ci.yml again and publishes the build (about 9 minutes).

Merging is a shipping action

Both long-lived branches publish, so a merge is a release, not just an integration:

  • dev publishes a v<series>.N-dev build to https://plg.wallacenet.ca/dev/latest.json, offered by the in-app updater to anyone on the Dev channel.
  • main runs release.yml and publishes a production release for everyone on the Production channel. Confirm before merging to main. release.yml runs assembleDebug only (no tests, no lint), so a main release is only as verified as the dev build it replays.

Katan

The island game is catalogue id catan, shown as Katan. It has two rule sets chosen at setup: the base game (docs/RULES-catan.md) and Cities & Knights (docs/RULES-catan-ck.md, code in game/catan/CatanCk*.kt, state behind CatanState.ck, which is null in the base game). The screen is layered; see docs/UI-catan.md before changing it.

Bash output is filtered before it reaches the model

.claude/settings.json registers a PreToolUse hook on Bash that rewrites each command through rtk: git status runs as rtk git status, and rtk compresses the output. Write commands normally — the rewrite is transparent and costs nothing to invoke.

Measured on this repo rather than quoted from rtk's README:

Command Raw Filtered Saved
git diff HEAD~3 521 KB 28 KB 95%
grep -rn "fun " app/src/main 117 KB 19 KB 84%
find . -name "*.kt" 7.7 KB 1.7 KB 79%
git log -20 20 KB 5.3 KB 74%
git diff --stat 4.0 KB 4.0 KB 1%
ls of a single package 50 B 62 B −24%

The saving is all in the large outputs, which is the point — a full diff of a few commits is otherwise most of a context window. Already-compact output grows slightly, because rtk adds a header; at those sizes it does not matter. rtk gain reports what the current session has actually saved.

Two limits are worth knowing. The Read, Grep and Glob tools are not Bash, so they never reach the hook and are unaffected; a deliberate rg or find in a shell is filtered. And filtering never truncates a file: cat becomes rtk read, which was checked byte-for-byte against this repo's longest Kotlin source (GameSetupScreen.kt, 946 lines) and came back identical. Commit messages survive the rewrite intact, quotes and colons included. To bypass filtering for one command, run rtk proxy <cmd>.

rtk is reinstalled at the start of every session

The container starts empty, so .claude/hooks/session-start.sh installs rtk before the session does anything. It pins RTK_VERSION, because the installer otherwise asks api.github.com for the latest tag and the proxy answers 403 for every repository except this one — the lookup fails and takes the install with it. Bump that variable by hand.

The hook is written not to matter when it fails. Outside the sandbox it installs nothing and just prints the command to run, since dropping a binary into someone's ~/.local/bin uninvited is not its business; if the download fails it says so and exits clean. With rtk absent the rewrite hook no-ops, and the only consequence is verbose output.