Playbook: Adding a New Repository to the Ecosystem
When to use
Use when creating any new GitHub repository under radieu/ that becomes part of the
Ecotrans / p24-infra ecosystem — web apps, Android apps, CLI tools, automation scripts,
libraries.
Pre-flight checklist
Before creating the repo, decide:
| Question | Notes |
|---|---|
| Repo name | kebab-case, descriptive, no abbreviations |
| Visibility | Private (default for all internal tooling); public only for OSS |
| Primary language | Kotlin, Python, TypeScript, etc. — drives .gitignore choice |
| Secrets needed? | If yes → plan a new SOPS file in p24-infra/secrets/ |
| Supabase migrations? | If yes → include supabase/migrations/ folder |
| CI/CD? | GitHub Actions → add standard error-notification secrets |
| Vercel deploy? | If Next.js → add to Vercel project after repo creation |
| CLAUDE.md needed? | Yes — always add one for Claude Code sessions in the new repo |
Step 1 — Create the GitHub repo
# Adjust --description and --gitignore as needed
gh repo create radieu/<repo-name> `
--private `
--description "<one-line description>" `
--gitignore <language> # e.g. Node, Python, Kotlin, SwiftVerify:
gh repo view radieu/<repo-name> --json name,visibility,defaultBranchStep 2 — Clone locally (if active development from workstation)
git clone git@github.com:radieu/<repo-name>.git C:\code_2026\<repo-name>
cd C:\code_2026\<repo-name>For agent-only repos (all work done via worktrees on VPS): skip local clone.
Step 3 — Initialize branch structure
The repo starts with main. Create dev and set it as default for PRs.
# Create dev branch from main
git checkout -b dev
git push origin dev
# Set dev as default branch (PRs target dev, not main)
gh api repos/radieu/<repo-name> -X PATCH -f default_branch=devVerify:
gh repo view radieu/<repo-name> --json defaultBranch
# Expected: {"defaultBranch":"dev"}Step 4 — Set up standard labels
$repo = "radieu/<repo-name>"
# Delete GitHub default labels (optional — keep if preferred)
@("bug","documentation","duplicate","good first issue","help wanted","invalid","question","wontfix") | ForEach-Object {
gh label delete $_ --repo $repo --yes 2>$null
}
# Create p24-infra standard labels
gh label create "bug" --repo $repo --color "d73a4a" --description "Something is broken"
gh label create "enhancement" --repo $repo --color "a2eeef" --description "New feature or improvement"
gh label create "plan" --repo $repo --color "5319E7" --description "Design/planning issue"
gh label create "human-action" --repo $repo --color "fbca04" --description "Waiting for human decision or action"
gh label create "queued" --repo $repo --color "0e8a16" --description "Queued for implementation"
gh label create "patch" --repo $repo --color "e4e669" --description "Small fix or tweak"
gh label create "ai-review-queued" --repo $repo --color "0075ca" --description "PR queued for AI auto-review"
gh label create "ai-review-rejected" --repo $repo --color "d93f0b" --description "PR rejected by AI review — issue re-queued"Step 5 — Add CLAUDE.md
Create a minimal CLAUDE.md at the repo root. Minimum required sections:
# CLAUDE.md — <Repo Name>
## Project Overview
<One-paragraph description of what this repo does and why.>
**GitHub repo:** `radieu/<repo-name>`
**Primary language:** <Kotlin / Python / TypeScript / etc.>
## Role
<What role does Claude play here? e.g. "Android Developer — builds and maintains the Kotlin app.">
## Repo structure
<Key folders and what they contain.>
## Commands
<How to build, test, run.>
## Secrets
<If applicable: which SOPS file holds credentials for this repo.>
Credentials: `p24-infra/secrets/<name>.env.sops`
## Branching
Same as p24-infra:
- `main` — tagged releases, never commit directly
- `dev` — integration branch, all PRs target here
- `feat/{issue}-{slug}`, `fix/{issue}-{slug}` — feature/fix branchesStep 5b — Set up .claude/ infrastructure (Claude Code permissions + skills)
Without this step, VS Code Claude Code extension will prompt permission for every tool call and
no project-specific slash commands (/sr, /commit, /create-pr) will be available.
.claude/settings.json
Copy the template below and adjust the allow list to match the repo’s stack
(remove Docker/SSH entries for pure app repos; keep ./gradlew for Android, pytest for Python):
$settingsPath = "C:\code_2026\<repo-name>\.claude\settings.json"
New-Item -ItemType Directory -Force (Split-Path $settingsPath) | Out-Null
[System.IO.File]::WriteAllText($settingsPath, @'
{
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git diff*)",
"Bash(git log*)",
"Bash(git add*)",
"Bash(git commit*)",
"Bash(git checkout*)",
"Bash(git merge*)",
"Bash(git branch*)",
"Bash(git push origin*)",
"Bash(git push -u origin*)",
"Bash(git fetch origin*)",
"Bash(git ls-remote*)",
"Bash(git show*)",
"Bash(git stash*)",
"Bash(git rebase*)",
"Bash(gh *)",
"Bash(ls *)",
"Bash(find *)",
"Bash(cat *)",
"Bash(grep *)",
"Bash(tail *)",
"Bash(head *)",
"Bash(wc *)",
"Bash(mkdir -p *)",
"Bash(cp *)",
"Bash(mv *)",
"Bash(touch *)",
"Bash(curl *)",
"mcp__github__get_file_contents",
"mcp__github__list_branches",
"mcp__github__list_commits",
"mcp__github__list_issues",
"mcp__github__list_pull_requests",
"mcp__github__get_commit",
"mcp__github__issue_read",
"mcp__github__pull_request_read",
"mcp__github__search_code",
"mcp__github__search_issues",
"mcp__github__search_pull_requests",
"mcp__github__get_me",
"mcp__github__add_issue_comment",
"mcp__github__issue_write",
"mcp__github__pull_request_review_write",
"mcp__github__create_branch",
"mcp__github__push_files",
"mcp__github__create_pull_request",
"mcp__github__update_pull_request",
"mcp__github__merge_pull_request",
"PowerShell",
"Edit",
"Write",
"Read",
"Glob",
"Grep"
]
}
}
'@, [System.Text.UTF8Encoding]::new($false))Stack-specific additions to the allow list:
| Stack | Add to allow |
|---|---|
| Android / Kotlin | "Bash(./gradlew *)", "Bash(adb *)" |
| Python | "Bash(python *)", "Bash(python3 *)", "Bash(pytest *)", "Bash(pip *)" |
| Node.js / Next.js | "Bash(npm *)", "Bash(node *)", "Bash(npx *)" |
| Server ops (SSH) | add full SSH/Docker block from p24-infra/.claude/settings.json |
.claude/commands/ — standard skill set
Copy three commands from p24-infra (they are generic enough to work in any repo):
$cmds = "C:\code_2026\<repo-name>\.claude\commands"
New-Item -ItemType Directory -Force $cmds | Out-Null
# sr — session report
Copy-Item "C:\code_2026\p24-infra\.claude\commands\sr.md" "$cmds\sr.md"
# commit — auto-commit with conventional message
Copy-Item "C:\code_2026\p24-infra\.claude\commands\commit.md" "$cmds\commit.md"For create-pr.md: copy from p24-infra/.claude/commands/create-pr.md — the pipeline is
milestone-only (the GitHub Projects v2 board sync was removed 2026-07-08). Adjust the --base
argument if the target repo uses dev instead of main as its PR base branch.
Verification
# Confirm files created
Get-ChildItem "C:\code_2026\<repo-name>\.claude" -Recurse | Select-Object FullName
# Expected: settings.json, commands/sr.md, commands/commit.md, commands/create-pr.mdOpen the repo in VS Code → type / in Claude Code chat → confirm sr, commit, create-pr
appear in the autocomplete list.
Step 6 — Add AI agent collaborators and accept invitations
Send invitations from your local workstation (as radieu):
$repo = "radieu/<repo-name>"
gh api repos/$repo/collaborators/AI-Dev-BMS4-1 -X PUT -f permission=write
gh api repos/$repo/collaborators/AI-Dev-BMS4-2 -X PUT -f permission=writeAccept from bms-4 — runners are authenticated as their AI-Dev accounts and can accept directly:
$acceptScript = @'
#!/bin/bash
sleep 5 # wait for GitHub to propagate invitation
for USER in claude-runner claude-runner-2; do
su -s /bin/bash $USER -c '
for id in $(gh api /user/repository_invitations --jq ".[].id" 2>/dev/null); do
gh api /user/repository_invitations/$id --method PATCH
echo "Accepted $id as $(gh api /user --jq .login)"
done
'
done
'@
[System.IO.File]::WriteAllText("$env:TEMP\accept-invites.sh", $acceptScript, [System.Text.UTF8Encoding]::new($false))
scp "$env:TEMP\accept-invites.sh" root@54.36.123.110:/tmp/accept-invites.sh
ssh root@54.36.123.110 "bash /tmp/accept-invites.sh"Verify:
gh api repos/$repo/collaborators --jq ".[].login"
# Expected: radieu, AI-Dev-BMS4-1, ai-dev-bms4-2If a runner’s OAuth token has expired, re-authenticate using the device flow:
docs/playbooks/worker-github-invite-acceptance.md §One-time setup
Step 7 — Register in dev_r_services (Supabase)
Using the canonical insert pattern from docs/playbooks/dev-r-services-insert.md.
Choose service_type and element_type from the table in that playbook:
| Repo type | service_type | element_type |
|---|---|---|
| Android / mobile app | workflow | script |
| Python automation | workflow | script |
| Next.js web app | workflow | workflow |
| CLI tool / library | workflow | script |
| Monitoring component | monitoring | container |
Add-Type -AssemblyName System.Web.Extensions
$ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
$lines = sops --decrypt --input-type dotenv --output-type dotenv C:\code_2026\p24-infra\secrets\monitoring.env.sops 2>$null
$token = ($lines | Where-Object { $_ -match "^SUPABASE_ACCESS_TOKEN=" } | Select-Object -First 1) -replace "^SUPABASE_ACCESS_TOKEN=", ""
$ref = "mwkqmgadqnkkihjdeqsi"
$url = "https://api.supabase.com/v1/projects/$ref/database/query"
$sql = @"
DO `$`$
BEGIN
IF NOT EXISTS (SELECT 1 FROM dev_r_services WHERE service_name = '<repo-name>') THEN
INSERT INTO dev_r_services (
service_name, project_id, project_name,
office_id, ws_id, app_id,
service_type, element_type, status, owner,
compliance_workbook, workbook_url, compliance_notes
) VALUES (
'<repo-name>', 'p24-infra', 'p24-infra',
'p24-devops', '99', 'et-app',
'<service_type>', '<element_type>', 'active', 'radieu',
'yes', 'docs/<repo-name>-operations.md',
'<one-line note>'
);
END IF;
END `$`$;
"@
$body = $ser.Serialize(@{query = $sql})
$bytes = [System.Text.Encoding]::UTF8.GetBytes($body)
$req = [System.Net.WebRequest]::Create($url)
$req.Method = "POST"; $req.ContentType = "application/json"
$req.Headers.Add("Authorization", "Bearer $token")
$req.ContentLength = $bytes.Length
$s = $req.GetRequestStream(); $s.Write($bytes, 0, $bytes.Length); $s.Close()
try {
$result = (New-Object System.IO.StreamReader(($req.GetResponse()).GetResponseStream())).ReadToEnd()
"OK: $result"
} catch [System.Net.WebException] {
$err = (New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())).ReadToEnd()
"ERR: $err"
}Verify insert:
# Run a SELECT query using same Invoke-SbQuery helper
# Expected: one row with service_name = '<repo-name>'Step 8 — Update ecosystem docs
C:\code_2026\CLAUDE.md — Projects table
Add a row:
| `<repo-name>` | `radieu/<repo-name>` | <one-line role> | <Stack> |C:\code_2026\p24-infra\docs\infrastructure-overview.md
Add a subsection under the relevant group (Apps / Tools / Scripts) describing the new repo.
Step 9 — Language-specific setup
Kotlin / Android
# The .gitignore is already created by gh repo create --gitignore Kotlin
# Add standard Android extras to .gitignore:
$extras = @"
# Android/Gradle
local.properties
*.iml
.idea/
.gradle/
build/
app/build/
captures/
.externalNativeBuild/
.cxx/
*.apk
*.aab
*.ap_
"@
# Append via [System.IO.File]::WriteAllText (avoids CRLF issues)Initial Android project:
- Create in Android Studio: File → New Project → Empty Views Activity (Kotlin)
- Set minimum SDK to API 26 (Android 8.0) unless lower is needed
- Push the generated project to
devbranch
Python CLI / automation
# pyproject.toml with ruff config (line-length=100, target-version=py311)
# requirements.txt / requirements-dev.txt
# pytest.ini pointing to tests/Next.js / TypeScript
npx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"Step 10 — (Optional) SOPS secrets file
If the new repo needs credentials:
# Create a new SOPS file for this repo's secrets
# See: docs/secrets-management.md §"When you add a NEW secret"
# File: p24-infra/secrets/<repo-name>.env.sops
# Keys example:
# <REPO>_SUPABASE_URL=...
# <REPO>_SUPABASE_SERVICE_KEY=...
# After creating: update .sops.yaml path_regex to include the new file pattern
# Commit: only the encrypted .env.sops file, never plaintextStep 11 — (Optional) Transfer existing issues
To move issues from another repo (e.g., p24-infra) to the new repo:
# Transfer preserves body, comments, and most labels (by name match)
gh issue transfer <issue-number> radieu/<repo-name> --repo radieu/<source-repo>After transfer:
- The original issue gets a “transferred to” comment and closes with a redirect
- Labels that don’t exist in target repo are dropped — create matching labels first (Step 4)
- Milestones are NOT transferred — set them manually in the target repo
Verification checklist
-
gh repo view radieu/<repo-name>shows correct visibility and description -
devbranch exists and is the default - All 6 standard labels created
- CLAUDE.md committed on
dev -
.claude/settings.jsoncreated with stack-appropriate permissions -
.claude/commands/sr.md,commit.md,create-pr.mdpresent — verified in VS Code/dropdown - AI agent collaborators added and invitations accepted (verify with
gh api repos/$repo/collaborators --jq '.[].login') -
dev_r_servicesrow inserted (SELECT confirms 1 row) - Ecosystem CLAUDE.md updated
- Infrastructure overview updated
Escalation
If gh repo create fails: check gh auth status — ensure you are authenticated as radieu.
If dev_r_services insert fails: see docs/playbooks/dev-r-services-insert.md for trigger-enforced columns.
If issue transfer fails with “not authorized”: confirm AI agent is a collaborator on both repos.