Playbook: Git identities & role responsibilities (commit → PR → review → merge → deploy)

Status: active · Owner: p24-infra · Review: every 90 days or on any identity change Scope: every repo operated from p24-infra sessions (p24-infra, et-operational-platform, brandpilot, Art-Agency, radekkonarski-personal-brand, gmail-tools, whatsup-android-chat-puller) + GitLab pinbox24/*.

Single source of truth for “which identity performs which Git action”. Other docs link here and must not restate the matrix. This playbook does not restate branch/commit format, review-pipeline mechanics, or per-host provisioning detail — it links to the canonical home of each (see §13).

Trigger — read this when you need to answer any of: who commits / who opens the PR / who reviews / who approves / who merges / who (really) deploys, for a given role and repo; or you are debugging a self-approval collision, a Vercel author-block, or a stuck review.


1. Identity registry

One row per identity. Credential sources are named by key/file NAME only — never a value.

IdentityKindRuns onGitHub/GitLab loginGit author (name / email)Credential sourceRotation
radieuhumanlap2 (active interactive host), windows-dev (dormant until ~2026-10-15, then 2nd interactive host)radieuRadek Konarski / radieu@gmail.comgh keyring—
AI-Dev-BMS4-1worker botbms-4 OS acct claude-runnerAI-Dev-BMS4-1AI-Dev-BMS4-1 / ai-dev-bms4-1@zintegrowana.online (repo-local since #7614) [D1] [VERIFY]gh device flow + App-2109526 clone token180d
ai-dev-bms4-2worker botbms-4 OS acct claude-runner-2ai-dev-bms4-2ai-dev-bms4-2 / ai-dev-bms4-2@zintegrowana.online (repo-local since #7614) [D1]same180d
AI-Dev-IO1worker botvps-i1 OS acct claude-runnerAI-Dev-IO1AI-Dev-IO1 / ai-dev-io1@zintegrowana.online (repo-local since #7614) [D1] [VERIFY]same180d
ai-dev-windev-1bot (interactive/automation identity)windows-dev (dormant), lap2 [D6]ai-dev-windev-1not set globally — machine git stays radieu; per-repo/per-command onlygh device flow180d
p24-reviewerGitHub App — review-onlyreview-pr workerApp 5208530 / inst. 168439358n/a (reviews, never commits)github-reviewer-app.env.sopsregenerate private key (Tier 3)
p24-infra-admin (App 2109526)GitHub App — broadCI, worker clone, gh secret set, n8n, counting PR approve [D5]App / inst. 142989418n/aGH Secrets / SOPS App-key entrieskey rotate
p24-n8n-issues [VERIFY]GitHub App — n8n issuesn8n workflowsAppn/an8n-bms4.env.sops (N8N_GITHUB_APP_*)key rotate
GITLAB_ADMIN_PATGitLab PAT (group Owner)bms-4 infra-task workergroup pinbox24 Ownern/apinbox24-gitlab.env.sops (narrow bms-4 copy, #4796)gitlab-token-rotation
CI tokenCIGH Actionsgithub-actions / App 2109526n/aGH Secrets—
deploy SSH keykeyCI → serversrootn/aVPS_SSH_PRIVATE_KEYper rotation matrix
Vercel / Cloudflare deploy tokensprovider tokensCIn/an/aGH Secrets / SOPSper rotation matrix

[VERIFY] = asserted but not yet confirmed against an authoritative source (a live git log on the named host, or an App/registry listing). p24-n8n-issues currently appears only in docs/secrets-rotation-log.md (App added 2026-10-08, #7481); the AI-Dev-BMS4-1 / AI-Dev-IO1 author rows await a live git log observation on their own hosts (the ai-dev-bms4-2 row is observed here).

Git author, post-#7614 ([D1]). On main, squash-merge commits are authored by the PR-opening bot with a @zintegrowana.online address (verified 2026-10-08: PR #7604 squash commit author ai-dev-bms4-2 <ai-dev-bms4-2@zintegrowana.online>, committer GitHub, signature verified). Worker branch commits were previously authored radieu <radieu@gmail.com>; #7613 / PR #7614 (merged 2026-10-08) fixed this. spawn-worker.sh now writes a repo-local identity on the per-issue clone: user.name = the worker’s GitHub login, user.email = <login-lowercased>@zintegrowana.online (the D1 target). [VERIFY] on the next worker PR with git log --format='%an|%ae|%cn|%G?' — this doc’s own branch commit is authored ai-dev-bms4-2 <ai-dev-bms4-2@zintegrowana.online>, confirming the mechanism is live in this clone.

Worker-to-OS-account and auth-method detail is owned by worker-gh-auth-standard.md §Worker registry — not restated here.


2. Role × action matrix

Rows = session roles (see CLAUDE.md §Role Enforcement) + human-owner. A cell names the identity used, or ✗ if the action is forbidden for that role. Enforcement level per repo is in §3.

Actiondev-coderdev-reviewerdev-testersys-adminsecret-managerhuman-owner
Commit to feature branchworker bot✗worker bot (tests)worker botworker bot (SOPS files)radieu
Push branchworker bot✗worker botworker botworker botradieu
Open PRworker bot✗worker botworker botworker botradieu
Post review comments✗p24-reviewer✗✗✗radieu
Approve✗verdict: p24-reviewer · counting approve: App 2109526✗✗✗radieu [D4]
Merge to main (p24-infra)✗review-pr worker [D2]✗✗✗radieu (--admin)
Merge to integration branch (app repos)✗review-pr worker [D2]✗✗✗radieu
Tag / release✗✗✗worker/CI✗radieu
Deploy (CI-triggered by merge)— automatic (see §8)—————
Deploy (manual, # PLAYBOOK:-annotated)✗✗✗sys-admin (SSH)✗radieu
SOPS write / rotate✗✗✗✗ (read key NAMES only)secret-manager✗
GitLab MR merge (pinbox24/*)✗✗✗infra-task worker (+ coupled-deploy check)✗radieu

Notes:

  • Approval is dual-identity (decided 2026-10-08, D5): p24-reviewer (least-privilege review-only App) posts the council review comments + verdict, but its approve does not count toward required_approving_review_count on main — so App 2109526 (p24-infra-admin) posts the count-satisfying approve. A human (radieu) approve also counts. No session role self-approves its own worker-authored code.
  • p24-reviewer posted its first real approval 2026-10-08 (PR #7604), alongside the App 2109526 approve that actually satisfied the branch-protection gate (see §6). Merged PRs #7614 and #7615 followed the same dual-approve pattern.
  • sys-admin/sys-security may read SOPS key names for verification; they never write SOPS.

3. Flow per repo type (+ enforcement level)

Enforcement level = how the “approve-before-merge” rule is enforced:

  • hard — GitHub branch protection (required_approving_review_count: 1, require_last_push_approval: true, dismiss_stale_reviews: true). The platform blocks a non-conforming merge.
  • process — no branch protection available (GitHub Free private repo); the rule is enforced by worker-prompt discipline + a detective post-merge audit, not by GitHub.
Repo typeBranch basePR targetPromotion chainMergerEnforcement
p24-infraorigin/mainmain (direct — no dev step)nonereview-pr worker [D2] / radieuhard
et-operational-platformorigin/devdevdev → rc-staging (throwaway) → staging → rc → mainreview-pr worker [D2] / radieuprocess (until #7612 E1–E6 land)
Pinbox24 GitLab (pinbox24/*)developmentdevelopmentGitLab CI on bms-1 (merge ⇒ deploy, see §8)infra-task worker (sys-admin)process (group Owner PAT)
  • p24-infra branching rule (single main, feat/*|fix/*|patch/*|plan/* from origin/main) and the frozen origin/dev are owned by standards/domains/branching.md (top note). et-op promotion gates (/staging, /staging-review-to-rc, /rc-to-main, lock ledger et-op#1918) are owned by et-op’s own CLAUDE.md — do not restate them here.
  • et-op approval is required before merge too (decided 2026-10-08). Because et-op is a GitHub Free private repo with no branch protection, enforcement is process: workers must open-PR-and-stop, and only the review-pr worker merges after a p24-reviewer/human APPROVE on the current head sha. The enforcement redesign (replace worker self-merge, add a merge-gate, add a post-merge audit) is tracked as E1–E6 on #7612 — until those land, the rule is honour-system.

4. Commit rules

  • Author identity per host: target ai-dev-*@zintegrowana.online for worker commits (D1 — see §1 observed-vs-target). Human interactive commits are radieu <radieu@gmail.com>.
  • Signing: [D3] open — observed main squash commits are GitHub web-flow signed (verified); no local per-role signing today. Tracked, not yet decided.
  • Format, closing keywords, native-linker phrasing → owned by standards/domains/branching.md and CLAUDE.md §Branching. The Co-Authored-By: attribution trailer is defined there (do not restate or hard-code a model name here). Use Partially implements: #N for a deliberate subset; keep close/fix/resolve words out of the ~3 words before any bare #N.
  • Never commit from the orchestrating checkout when a worktree is the convention (CLAUDE.md §Agent Workflow — Worktree Branch Isolation).

5. PR rules

Title/body format, labels (ai-review-queued, human-action, blocked-on-dependency), draft policy, and the partial-scope keyword are owned by branching.md + CLAUDE.md §Branching and the worker prompt (infra/agent-prompts/worker-issue.md Steps 8–9). Not restated here. The one identity fact: a PR is opened by the worker bot (or radieu for a human PR); dev-reviewer never opens PRs.

6. Review & approval rules

  • Reviewer identity (review verdict): p24-reviewer (App 5208530) is the review identity — the review-pr worker mints its token (scripts/gh-app-token --app reviewer, key in github-reviewer-app.env.sops) and runs gh pr review --approve to post the council review comments and verdict. It holds only pull_requests:write / contents:read / issues:write / metadata:read.
  • Its approve does NOT count toward the merge gate (decided 2026-10-08, D5). On the real PRs #7604, #7606–#7608 the gh pr review --approve returned exit 0 but reviewDecision stayed REVIEW_REQUIRED and the review’s authorAssociation was NONE — a p24-reviewer approve alone leaves the PR BLOCKED. Probable cause: the App has contents:read only, so GitHub does not treat it as an eligible reviewer for required_approving_review_count (inferred from the logs, not proven). The decision is not to grant p24-reviewer contents:write.
  • Counting approve: App 2109526 (p24-infra-admin) posts the count-satisfying approve — the dual-approve pattern (p24-reviewer verdict + App 2109526 approve). App 2109526 never pushes PR branches, so its approve meets require_last_push_approval. A human (radieu) approve also counts ([D4] = allow). How-to is owned by reviewer-github-app.md; live state by reviewer-github-app-operations.md.
  • Fallback chain (when the App 2109526 token cannot be minted): last resort is a worker-identity approve, which may self-collide and then needs a human merge click. Mechanics + the residual collision case are owned by ai-pr-auto-review.md and CLAUDE.md §Branching.
  • require_last_push_approval + dismiss_stale_reviews: an approval is bound to the head sha; any push after it dismisses prior reviews. The worker’s synchronize re-queue (#6387) re-reviews after a post-approval push so it never deadlocks. Full semantics → ai-pr-auto-review.md.

7. Merge rules

  • Who merges ([D2]): the review-pr worker merges PRs — worker-authored and human-authored — only after a valid APPROVE. A human (radieu) may also merge (--admin reserved to human-owner). Workers in other roles do not self-merge (the et-op self-merge path is being removed under #7612 E1).
  • Method: squash + delete branch on p24-infra. (A --merge vs --squash inconsistency between the worker prompts and the review-pr worker is tracked as #7612 E6.)
  • Failure path: if the App token cannot be minted and a same-account approve self-collides, the merge stops and a human merge click is required — state it as a fact, do not loop gh pr merge (CLAUDE.md §Branching).

8. Deploy rules

Deploy is a consequence of merge, not an identity. No role “deploys” — a merge fires a workflow that deploys under a service credential.

TriggerWorkflow / mechanismRunnerCredential (NAME)Target
Merge to main (config/secrets)secrets-sync.yml, deploy-monitoring-config.ymlGH Actions → SSHAGE_KEY_GHA, VPS_SSH_PRIVATE_KEYvps-i1 / bms-4
Merge to main (CF Worker)wrangler deploy in CIGH ActionsCF deploy token (cloudflare.env.sops / CF_WORKERS_EDIT_TOKEN)Cloudflare
Merge to Vercel-tracked branchVercel git integration or reusable ActionVercel / GH ActionsVERCEL_TOKENVercel
GitLab MR merge to a deploy-coupled branch.gitlab-ci.yml deploy stageGitLab runner (bms-1)runner credsPinbox24 prod (~30 s)
  • Vercel commit-author seat requirement [D7]: Vercel’s git integration gates auto-deploy on the merging commit’s GitHub identity holding a contributing Vercel seat — a bot author without one is silently blocked. The decided direction is author-independent deploy via a VERCEL_TOKEN Action (.github/workflows/reusable-deploy-vercel.yml, #7592), which bypasses the seat check, rather than buying bot seats. Root cause + rollout owned by vercel-deploy-blocked-git-author-access.md; Vercel access for ai-dev-bms4-2 is tracked on #7590 (D7).
  • GitLab merge ⇒ deploy: merging a pinbox24/* MR whose target branch couples deploy in .gitlab-ci.yml deploys to production within ~30 s with no intercept window. Always check the target branch’s .gitlab-ci.yml for a coupled deploy stage before merging. Owned by w3-w4-stack-operations.md §2.

9. Per-host operating profile

For each host: default gh account, git config, allowed roles, how to switch. The authoritative per-host detail lives in docs/environments/<host>.md — this section links, it does not copy.

HostDefault gh / git identityRolesNotes
lap2radieuhuman-owner, any interactive roleActive interactive host. lap2.md
windows-devradieu (both radieu + ai-dev-windev-1 logged in; switch with gh auth switch)human-owner, interactiveDormant until ~2026-10-15, then 2nd interactive host. Git identity stays radieu globally. windows-dev.md
dev-workstationradieuhuman-ownerdev-workstation.md
bms-4 (claude-runner)AI-Dev-BMS4-1dev-coder, dev-reviewer, dev-tester, sys-admin, secret-manager, infra-taskprimary dispatch. ai-dev-bms4-1.md, bms-4.md
bms-4 (claude-runner-2)ai-dev-bms4-2same2nd worker account.
vps-i1 (claude-runner)AI-Dev-IO1monitoring-ops, fallback workerai-dev-io1.md

Per-host env docs may still describe windows-dev as the active box / lap2 as new — aligning those status banners with the decided lap2-active / windows-dev-dormant state is a separate doc-sweep (do not rewrite them from this playbook).

10. Verification commands

# Who am I (this host)
gh auth status
git config user.name; git config user.email
 
# Who authored / committed / signed recent commits on main
git log origin/main --no-merges --format='%h|%an|%ae|%cn|%G?' -n 20
 
# Who opened vs merged recent PRs (identity split)
gh pr list --repo radieu/p24-infra --state merged --limit 15 \
  --json number,author,mergedBy,title \
  --jq '.[] | "#\(.number) author=\(.author.login) mergedBy=\(.mergedBy.login // "?")"'
 
# Who approved a given PR (expect p24-reviewer[bot] primary)
gh api repos/radieu/p24-infra/pulls/<N>/reviews \
  --jq '.[] | "\(.user.login) [\(.user.type)] \(.state)"' | sort -u
 
# Worker gh identity on each SSH worker → worker-gh-auth-standard.md §Verify

11. Exceptions & escalation

SituationWhat it meansOwning playbook
p24-reviewer approve alone leaves PR BLOCKEDexpected — the review-only App approve does not count toward required_approving_review_count; the worker posts the App 2109526 approve to satisfy the gate (dual-approve)§6, ai-pr-auto-review.md
Self-approval collision (require_last_push_approval fails after a worker-identity approve)reviewer == pusher account; mint App-2109526 approve, else human mergeCLAUDE.md §Branching, ai-pr-auto-review.md
Vercel deploy silently blockedmerging commit author has no Vercel seat → use the token Actionvercel-deploy-blocked-git-author-access.md
Stuck ai-review-queued (no reviewer comment)wait a dispatcher cycle before escalating; don’t self-reviewCLAUDE.md §Branching
p24-reviewer token mint failurefall back to App 2109526, then worker, then humanreviewer-github-app.md
GitLab MR would deploy on mergecoupled deploy stage — confirm before mergingw3-w4-stack-operations.md §2
Auto Mode classifier blocks an identity-switching approvesurface to the user; don’t work aroundCLAUDE.md §Branching (Auto Mode note)

12. Open decisions

Resolved 2026-10-08 (user): D1 worker author = ai-dev-*@zintegrowana.online (shipped by #7613 / PR #7614) · D2 review-pr worker merges PRs (incl. human-authored) only after approve · D4 a human may approve · D5 p24-reviewer stays review-only and is not granted contents:write; its approve does not count toward the merge gate, so App 2109526 (p24-infra-admin) remains the counting approver (dual-approve pattern) in addition to CI / worker clone / gh secret set / n8n.

Still open (tracked, not blocking this doc):

#DecisionStatus
D3Commit signing (off / per-role SSH signing / web-flow only)TBD — web-flow signing observed on main
D6Register ai-dev-windev-1 on lap2 too, or windows-dev onlyTBD
D7Vercel seats for bot authors vs token-Action deploytoken-Action direction chosen (#7592); seat for ai-dev-bms4-2 tracked #7590

13. References


First written for #7611. Keep the matrix here canonical — update this file on any identity change, and link to it from docs that previously carried a fragment of it rather than copying the matrix back.