Only confirmed correctness bugs block approval; everything else is a note. At most one fix round, then hand off. Don't widen scope to satisfy a review. Merge main only on conflict or request. The token goes in a private header file, and PR responses are checked for a number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.1 KiB
claude-playground
A tiny zero-dependency Node.js todo CLI, used as a sandbox for trying Claude Code features.
Commands
npm test— run the test suite (built-innode:test, no install needed)npm start -- add "something"/npm start -- ls— run the CLI (data intodos.json, override withTODO_FILE)
Conventions
- ES modules, Node >= 20, no third-party dependencies.
src/todo.jsholds pure functions; keep I/O inload/saveandsrc/cli.js.- Every behavior change gets a test in
test/. - Commit messages: short imperative subject line.
Workflow (required)
Never commit, merge, or push to main. Only a human (Christian) merges into main, in Gitea.
The git hooks installed by npm install and Gitea branch protection enforce this (see "Protecting main" in the README). Never get around them: no --no-verify, no changing core.hooksPath, no editing .git/.
- Branch. Run
git fetch originon its own, thengit switch --no-track -c <type>/<short-name> origin/main(e.g.fix/duplicate-ids). - Commit, test, push.
npm testmust pass. Check thatgit branch --show-currentisn'tmain, then rungit push -u origin HEAD. - Open the PR, once per branch (see "Gitea API").
- Review with
/code-review <branch> medium. - Triage. The review lists unverified candidates, and it will always find some. Check each one against the code. A finding blocks only if it is a correctness bug in this PR's change that you have confirmed, ideally with a test that fails. Everything else is a note: cleanups, judgement calls, hardening ideas and unconfirmed findings.
- Comment on the PR:
- First line: Needs fixes if anything blocks, otherwise Approved.
- Second line:
_Automated self-review by Claude; not an independent review._ - Then the blocking findings, then the notes, then the findings you rejected, each with the reason.
- At most one fix round. If the PR needs fixes, fix only the blocking findings, each with a test. Push, review once more, and post the verdict from steps 5–6. Then stop, whatever that second review says. Findings about the fixes themselves go to the human as notes; don't fix them.
- Hand off. Never merge, even if the PR is approved. Tell the human it's ready, with the verdict and any notes.
Don't widen scope beyond the PR's purpose to satisfy a review. If a fix would add significant new code (a new mechanism, not a correction), list it as a note for the human instead.
Bring in the latest main only when the PR conflicts or the human asks:
- Run
git fetch origin. - Run
git merge --no-ff --no-commit origin/main. - If it says "Already up to date", stop here. Otherwise resolve any conflicts and run
npm test. - Run
git commit --no-edit.
Run each as its own command, and never rebase or force-push.
Gitea API
No gh/tea: use curl with $GITEA_TOKEN.
One-time human setup in the git-ignored .claude/settings.local.json: set env.GITEA_TOKEN, and add code.westerkam.org to sandbox.network.allowedDomains. Then restart the session (claude --continue); settings load only at start. If the token is unset, stop and ask the human.
Rules for every API call:
- Make each call one self-contained command, because shell state doesn't carry over between commands.
- Put titles and bodies in files in
$TMPDIR(never in the repo) and pass them with--rawfile, never inline in double quotes, where backticks and$(…)would run. - The token goes in a private header file, so it doesn't show up in the process list.
pipefail,curl -fand thejqchecks make every failure exit non-zero.
# Open a PR for the current branch (once). Write $TMPDIR/pr-title.txt and $TMPDIR/pr-body.md first.
set -o pipefail; API=https://code.westerkam.org/api/v1/repos/christian/claude-playground
(umask 077; printf 'Authorization: token %s\n' "$GITEA_TOKEN" > "$TMPDIR/gitea-auth")
jq -n --arg head "$(git branch --show-current)" --rawfile title "$TMPDIR/pr-title.txt" --rawfile body "$TMPDIR/pr-body.md" \
'{head:$head, base:"main", title:($title|rtrimstr("\n")), body:$body}' \
| curl -fsS -X POST -H @"$TMPDIR/gitea-auth" -H "Content-Type: application/json" -d @- "$API/pulls" \
| jq -er 'if .number then "#\(.number) \(.html_url)" else error("no PR number in response") end'
# Comment on PR <number>. Write $TMPDIR/comment.md first.
set -o pipefail; API=https://code.westerkam.org/api/v1/repos/christian/claude-playground
(umask 077; printf 'Authorization: token %s\n' "$GITEA_TOKEN" > "$TMPDIR/gitea-auth")
jq -n --rawfile body "$TMPDIR/comment.md" '{body:$body}' \
| curl -fsS -X POST -H @"$TMPDIR/gitea-auth" -H "Content-Type: application/json" -d @- "$API/issues/<number>/comments" \
| jq -er .html_url
Commands listed under sandbox.excludedCommands in .claude/settings.json (git fetch, git commit, git push and others) run outside the sandbox only when they are run standalone: no cd … && before them and nothing chained after. They need that to reach the remote and to sign commits.
Remote
Gitea at code.westerkam.org — origin = git@code.westerkam.org:christian/claude-playground.git.