Document branch-and-PR workflow; never write to main #1

Merged
christian merged 5 commits from docs/branch-pr-workflow into main 2026-10-21 00:58:41 +02:00
Showing only changes of commit fa45c58c0c - Show all commits
+14 -10
View File
@@ -14,27 +14,31 @@ A tiny zero-dependency Node.js todo CLI, used as a sandbox for trying Claude Cod
## Workflow (required) ## Workflow (required)
Never commit, merge, or push to `main`. Only a human (Christian) merges into `main`. Never commit, merge, or push to `main`. Only a human (Christian) merges into `main`.
This is also enforced outside this file: Gitea branch protection on `main` and a deny rule in `.claude/settings.json`. These rules are instructions, not a lock. Gitea branch protection on `main` is the real guard; the `git push … main` deny rules in `.claude/settings.json` only catch the obvious spellings.
1. Start every change from a fresh `main`: `git fetch origin && git switch -c <type>/<short-name> origin/main` (e.g. `fix/duplicate-ids`, `docs/readme`). 1. Start every change from a fresh `main`. Run `git fetch origin` as its own command, then `git switch --no-track -c <type>/<short-name> origin/main` (e.g. `fix/duplicate-ids`, `docs/readme`). `--no-track` keeps the branch from tracking `main`.
2. Commit on that branch; run `npm test` before every push. 2. Commit on that branch; run `npm test` before every push.
3. Push the branch and open a PR against `main` (see "Gitea API" below). 3. Push with `git push -u origin HEAD`, then open a PR against `main` (see "Gitea API" below).
4. Review it with `/code-review <branch>`. Always name the branch: with no target, the review diffs against the just-pushed upstream and sees nothing. 4. Review it with `/code-review <branch>`. Always name the branch, so the review diffs it against `main` whatever its upstream is.
5. Post the result as a PR comment. Its first line is **Approved** or **Needs fixes**, its second is `_Automated self-review by Claude; not an independent review._`, then the findings. 5. Post the result as a PR comment. The first line is the verdict: **Needs fixes** if there is any correctness finding, otherwise **Approved** (cleanup-only findings are listed but don't block). The second line is `_Automated self-review by Claude; not an independent review._`, followed by the findings.
6. If it needs fixes, fix the correctness findings on the same branch and repeat steps 2–5. At most 2 fix rounds: after that, post what's left and hand off to the human. 6. If it needs fixes, fix the correctness findings on the same branch and repeat steps 2–5. At most 2 fix rounds: after that, post what's left and hand off to the human.
7. Stop there. Do not merge the PR, even if approved; tell the human it's ready. 7. Stop there. Do not merge the PR, even if approved; tell the human it's ready.
### Gitea API ### Gitea API
No `gh`/`tea`: use `curl` with `$GITEA_TOKEN` (set in the git-ignored `.claude/settings.local.json`). If it's unset, stop and ask the human; don't work around it. 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`. Settings load at session start, so after adding them the session must be restarted (`claude --continue`). If the token is unset, stop and ask the human; don't work around it.
Shell state doesn't persist between commands, so set every variable in the same command as the call. Write comment bodies to `$TMPDIR`, never into the repo. `-fsS` makes HTTP errors fail loudly; check that the PR response has a `.number`.
```sh ```sh
API=https://code.westerkam.org/api/v1/repos/christian/claude-playground API=https://code.westerkam.org/api/v1/repos/christian/claude-playground
# Open a PR # Open a PR
BRANCH=fix/x TITLE="Fix x" BODY="…"
jq -n --arg head "$BRANCH" --arg title "$TITLE" --arg body "$BODY" '{head:$head, base:"main", title:$title, body:$body}' \ jq -n --arg head "$BRANCH" --arg title "$TITLE" --arg body "$BODY" '{head:$head, base:"main", title:$title, body:$body}' \
| curl -s -X POST -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d @- "$API/pulls" | curl -fsS -X POST -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d @- "$API/pulls" | jq -e .number
# Comment on PR number $N # Comment on PR number $N
jq -n --rawfile body comment.md '{body:$body}' \ N=1
| curl -s -X POST -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d @- "$API/issues/$N/comments" jq -n --rawfile body "$TMPDIR/comment.md" '{body:$body}' \
| curl -fsS -X POST -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d @- "$API/issues/$N/comments" | jq -r .html_url
``` ```
Run `git commit` and `git push` as standalone commands (no `cd … &&` prefix): only then do they run outside the sandbox, which commit signing needs. Run `git fetch`, `git commit` and `git push` as standalone commands (no `cd … &&` prefix, nothing chained after them): only then do they run outside the sandbox, which reaching the remote and commit signing need.
## Remote ## Remote
Gitea at code.westerkam.org — `origin` = `git@code.westerkam.org:christian/claude-playground.git`. Gitea at code.westerkam.org — `origin` = `git@code.westerkam.org:christian/claude-playground.git`.