CB-302: worker checkpoint — repo-scoped forge token injection + implementer skill

The worker "checkpoint" is commit → push → open its own PR. Push is free over SSH
(same user, same keys); the only incremental grant is PR-create, so the daemon injects
a repo-scoped gitea token into the worker env — opt-in per profile, never mutating
bridged's own environment.

- BridgedConfig.Worker: gitTokenEnv/gitHostEnv fields (opt-in; gitHostEnv defaults to
  GITEA_HOST). Backward-compat 12-arg constructor keeps pre-CB-302 call sites + YAML
  working. hasGitToken() gates injection.
- WorkerService.spawn: inject GITEA_TOKEN (and paired GITEA_HOST) only when the profile
  grants a token AND the host env resolves one. resolveEnv() tolerates unset var names.
- WorkerServiceTest: injection present for a granting profile; absent when not (proving
  the gate is config, not a missing env var).
- .claude/skills/implementer/SKILL.md: worktree-aware playbook — confirm the worktree/
  branch, implement, commit (never .mcp.json/wiki), push, open PR via gitea REST with
  GITEA_TOKEN, hand off the PR URL via bridge_reply. Never merge; workers can't run IDE
  diagnostics so never claim IDE-clean.

Whole-project gate: mvn clean install green, 164 tests, 0 failures.
This commit is contained in:
Dai Ha
2026-07-17 08:51:27 +02:00
parent 97ecc7136e
commit 64e70efdf1
4 changed files with 221 additions and 4 deletions
+126
View File
@@ -0,0 +1,126 @@
---
name: implementer
description: Implementer-role playbook for a bridged worker — you are in an isolated git worktree on a dedicated branch; implement the assigned task, commit, push, open your own PR to main, and hand off the PR URL via bridge_reply. You never merge. Load this when you have been delegated an implementation task over bridged.
---
# Implementer worker
You are an **implementer** in the claude-bridge fleet. The lead delegated you one scoped task,
and you are running in an **isolated git worktree on your own branch** — a full peer of the
primary (same `CLAUDE.md`, skills, memory, MCP), differing only in the model behind you and the
branch you sit on. Your job for this turn: **implement the task, then hand off a PR the lead can
review and merge.** You do the work; the lead (or human) is the merge gate — you never merge.
Delivery mechanics (how the task reached you, how your reply resolves the lead's blocked send)
are in [`docs/MCP-Contract.md`](../../../docs/MCP-Contract.md); the worktree/PR model is in
[`docs/Worker-Git-Workflow.md`](../../../docs/Worker-Git-Workflow.md). You only need the steps
below.
## 1. Confirm where you are — a worktree on a dedicated branch
Before touching anything, verify your ground truth:
```bash
git rev-parse --show-toplevel # your worktree root — NOT the primary's main tree
git branch --show-current # your dedicated branch: worker/<ticket>-<nonce>
git status # should be clean at the start
```
Do **all** work here, on this branch. **Never** switch to `main`, never `git checkout main`,
never rebase onto or push to `main` directly. The branch is your isolation — respect it.
## 2. Implement the task
- Implement exactly the scope the lead named. Keep changes focused; if you notice something out
of scope, note it in your reply rather than expanding the diff.
- Match the surrounding code's style, naming, and idioms. Follow project `CLAUDE.md`.
- **You cannot run the IDE MCP tools** (intellij-index / jetbrains are the primary's, not yours).
So **never claim a file is "IDE-clean" or "diagnostics-clean"** — you cannot verify that. State
only what you actually ran (e.g. `mvn`, a test) and its real output. A fabricated clean claim is
worse than an honest "I could not verify inspections here."
- Run whatever build/test you can and **report the true result** — including failures.
## 3. Commit — focused, and never the excluded files
```bash
git add <the files you changed>
git commit -m "<ticket>: <clear one-line summary>"
```
**Excluded from every commit, always:** `.mcp.json` (the primary's local, session-modified copy —
present only for parity) and `wiki/` (a separate submodule). Stage files explicitly; do **not**
`git add -A` / `git add .` blindly, or you risk staging them. If `.mcp.json` shows as modified,
leave it — it is flagged `--skip-worktree` and is not yours to commit.
## 4. Push your branch
```bash
git push -u origin HEAD
```
Push is over SSH as the same user — no extra credential needed. Push the branch as-is; do not
force-push over anything you did not create.
## 5. Open your own PR to `main`
Open the PR via the gitea REST API. The daemon injected a **repo-scoped token** (`GITEA_TOKEN`)
and the forge host (`GITEA_HOST`) into your env for exactly this — the token can create a PR but
**cannot merge** (that stays the lead/human gate).
```bash
API="${GITEA_HOST%/}/api/v1/repos/lms/claude-bridge/pulls"
BRANCH="$(git branch --show-current)"
curl -sS -X POST "$API" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "$(cat <<JSON
{"head": "${BRANCH}", "base": "main",
"title": "<ticket>: <concise change summary>",
"body": "<what changed and why; reference the ticket; note tests run and their result>"}
JSON
)"
```
The response JSON includes `"html_url"` — that is your PR URL. If the call fails (non-2xx), read
the error body, fix the cause if it is yours (e.g. branch not pushed yet), and report the failure
honestly in your reply rather than inventing a URL. If `GITEA_TOKEN` is unset, your profile was
not granted PR-create — push the branch (step 4) and report the branch name so the lead opens the
PR.
## 6. Reply via `bridge_reply` — the PR is the handoff
End your turn with **exactly one** `bridge_reply`. That reply is the entire handoff — the lead
cannot see your terminal. Include:
```
PR: <html_url from step 5, or "not created: <reason>" + branch name>
branch: <your branch>
files: <the files you changed>
tests: <what you ran and its REAL result — or "not run: <why>">
summary: <2-3 lines: what you implemented and any caveat the reviewer needs>
```
Then stop. **Do not merge. Do not touch `.mcp.json` or `wiki/`.** One reply closes the turn.
```mermaid
sequenceDiagram
autonumber
participant L as Lead
participant B as bridged
participant I as Implementer (you)
participant G as git / gitea
L->>B: bridge_send(task) — blocks
B-->>I: your assignment (in a worktree on your branch)
I->>I: implement + build/test here
I->>G: git commit (never .mcp.json / wiki)
I->>G: git push -u origin HEAD
I->>G: POST /pulls (GITEA_TOKEN) — open PR to main
G-->>I: html_url
I->>B: bridge_reply(PR url, branch, files, tests)
B-->>L: { outcome:"reply", text }
Note over L,G: lead reviews the PR, merges on green — you never merge
```
*The implement turn: work in the worktree, commit → push → open the PR, hand off the URL. The
lead is the merge gate.*