You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make the nightly Claude Code claude-routines open PRs as a non-admin GitHub identity so the main-protection CODEOWNERS gate (landed via #295) is actually enforced instead of bypassed. Today routines authenticate as schmug (repo admin); GitHub branch-ruleset bypass_actors keys off the admin role with bypass_mode: always, so every routine PR bypasses every protection. On a public repo whose single-issue routine prefers issues that read like Claude Code prompts (i.e. favors well-crafted external/attacker input), this is an open path from a stranger's issue to an auto-merged production deploy. Authoritative finding (Claude Code docs, verified 2026-05-16): routines always author PRs as the connected account; there is no per-routine identity config and no bot-author option — the only supported split is to repoint the claude.ai account's GitHub connection to a separate account.
Add dmarcheck-bot to schmug/dmarcheck as a write collaborator — NOT admin, NOT added to ruleset bypass_actors.
Repoint the claude.ai account's GitHub connection from schmug to dmarcheck-bot (account-wide; affects all cloud sessions).
Net: cloud routines + cloud interactive sessions act as dmarcheck-bot (non-admin). Local terminal Claude Code keeps local gh creds = schmug (admin) — unaffected. Accepted side effect: cloud interactive sessions become non-admin (owner does admin work locally).
main-protection ruleset (id 14716629): keep require_code_owner_review: true, require_last_push_approval: false, required checks = ["check"] only, bypass_actors = repository admin role / bypass_mode: always. Do NOT add dmarcheck-bot to bypass_actors. Do NOT re-add require_last_push_approval or CodeQL required contexts (see issue context: those deadlocked all merges on 2026-05-16).
BLOCKED until @schmug approves in the morning sweep (legitimate — schmug is not the author)
schmug admin + bypass_mode: always is retained as the by-design owner escape hatch for the sweep. Bot keeps the default claude/* push restriction (do not enable unrestricted branch pushes for it).
Ruleset: gh api repos/schmug/dmarcheck/rulesets/14716629 (classic branches/main/protection is 404 — it's a ruleset).
CLAUDE.md "Security" section — update to document the final identity model.
Memory/context: require_last_push_approval + CodeQL required contexts were removed 2026-05-16 because they deadlocked all merges; must not regress.
Constraints
dmarcheck-bot must NOT have admin and must NOT appear in ruleset bypass_actors, or the gate is moot.
@schmug (CODEOWNERS owner) must retain write access for the human sweep.
Do not re-introduce require_last_push_approval (non-path-scopable; breaks single-maintainer sweep) or the Analyze (actions) / Analyze (javascript-typescript) required contexts (NEUTRAL on Dependabot → deadlock).
Owner-only manual steps (cannot be automated): creating the GitHub account and repointing the claude.ai GitHub connection.
Acceptance criteria
dmarcheck-bot exists, 2FA on, added as write (not admin) collaborator on schmug/dmarcheck.
dmarcheck-bot is absent from main-protectionbypass_actors; ruleset still: require_code_owner_review:true, require_last_push_approval:false, required checks ["check"].
claude.ai GitHub connection repointed to dmarcheck-bot; a routine PR is authored by dmarcheck-bot, not schmug.
Verification: a throwaway dmarcheck-bot PR touching a CODEOWNERS path shows mergeStateStatus: BLOCKED until @schmug approves, then becomes mergeable.
Verification: a dmarcheck-bot PR touching only non-CODEOWNERS paths auto-merges on green check.
Verification: local terminal Claude Code still operates as schmug (admin) — gh api user --jq .login = schmug locally.
CLAUDE.md Security section updated to describe the final two-identity model (local=schmug admin, cloud=dmarcheck-bot non-admin) and the do-not-regress ruleset invariants.
Out of scope / follow-ups
Known residual (file as separate follow-up): CODEOWNERS is intentionally narrow, so a malicious-issue-driven PR touching only non-CODEOWNERS paths (e.g. a new exfiltrating module under src/analyzers/ other than the two listed, or src/orchestrator.ts) still auto-merges. Recommend a follow-up to broaden CODEOWNERS to src/analyzers/** + src/orchestrator.ts + src/shared/scoring.ts, weighed against autonomy. Not solved here — security: add CODEOWNERS human-review gate for autonomous-routine PRs #295 deliberately chose narrow scope.
Changing the routine's issue-selection / prompt-scoring heuristic.
Any runtime/app code change.
Migrating off Claude Code routines to GitHub Actions (alternative architecture B; rejected to preserve the routine+sweep workflow).
Task
Make the nightly Claude Code
claude-routinesopen PRs as a non-admin GitHub identity so themain-protectionCODEOWNERS gate (landed via #295) is actually enforced instead of bypassed. Today routines authenticate asschmug(repo admin); GitHub branch-rulesetbypass_actorskeys off the admin role withbypass_mode: always, so every routine PR bypasses every protection. On a public repo whose single-issue routine prefers issues that read like Claude Code prompts (i.e. favors well-crafted external/attacker input), this is an open path from a stranger's issue to an auto-merged production deploy. Authoritative finding (Claude Code docs, verified 2026-05-16): routines always author PRs as the connected account; there is no per-routine identity config and no bot-author option — the only supported split is to repoint the claude.ai account's GitHub connection to a separate account.Design (decisions locked)
Identity model
dmarcheck-bot(2FA enabled; recovery owned by@schmug).dmarcheck-bottoschmug/dmarcheckas a write collaborator — NOT admin, NOT added to rulesetbypass_actors.schmugtodmarcheck-bot(account-wide; affects all cloud sessions).dmarcheck-bot(non-admin). Local terminal Claude Code keeps localghcreds =schmug(admin) — unaffected. Accepted side effect: cloud interactive sessions become non-admin (owner does admin work locally).Enforcement (already half-built by #295)
main-protectionruleset (id 14716629): keeprequire_code_owner_review: true,require_last_push_approval: false, required checks =["check"]only,bypass_actors= repository admin role /bypass_mode: always. Do NOT adddmarcheck-bottobypass_actors. Do NOT re-addrequire_last_push_approvalor CodeQL required contexts (see issue context: those deadlocked all merges on 2026-05-16)..github/CODEOWNERS(merged via security: add CODEOWNERS human-review gate for autonomous-routine PRs #295) becomes live-enforced once the author is non-admin.Behavior matrix (intended end state)
check.github/**,package.json,package-lock.json,wrangler.toml,SECURITY.md,CLAUDE.md,.claude/settings.json,src/index.ts,src/rate-limit.ts,src/analyzers/mta-sts.ts,src/analyzers/security-txt.ts,src/db/)check+@schmugreview@schmugapproves in the morning sweep (legitimate —schmugis not the author)schmugadmin +bypass_mode: alwaysis retained as the by-design owner escape hatch for the sweep. Bot keeps the defaultclaude/*push restriction (do not enable unrestricted branch pushes for it).Pointers
.github/CODEOWNERS,CLAUDE.mdsecurity posture. This issue is the deferred "Bot-identity split — owner action."gh api repos/schmug/dmarcheck/rulesets/14716629(classicbranches/main/protectionis 404 — it's a ruleset).CLAUDE.md"Security" section — update to document the final identity model.require_last_push_approval+ CodeQL required contexts were removed 2026-05-16 because they deadlocked all merges; must not regress.Constraints
dmarcheck-botmust NOT have admin and must NOT appear in rulesetbypass_actors, or the gate is moot.@schmug(CODEOWNERS owner) must retain write access for the human sweep.require_last_push_approval(non-path-scopable; breaks single-maintainer sweep) or theAnalyze (actions)/Analyze (javascript-typescript)required contexts (NEUTRAL on Dependabot → deadlock).Acceptance criteria
dmarcheck-botexists, 2FA on, added as write (not admin) collaborator onschmug/dmarcheck.dmarcheck-botis absent frommain-protectionbypass_actors; ruleset still:require_code_owner_review:true,require_last_push_approval:false, required checks["check"].dmarcheck-bot; a routine PR is authored bydmarcheck-bot, notschmug.dmarcheck-botPR touching a CODEOWNERS path showsmergeStateStatus: BLOCKEDuntil@schmugapproves, then becomes mergeable.dmarcheck-botPR touching only non-CODEOWNERS paths auto-merges on greencheck.schmug(admin) —gh api user --jq .login=schmuglocally.CLAUDE.mdSecurity section updated to describe the final two-identity model (local=schmugadmin, cloud=dmarcheck-botnon-admin) and the do-not-regress ruleset invariants.Out of scope / follow-ups
src/analyzers/other than the two listed, orsrc/orchestrator.ts) still auto-merges. Recommend a follow-up to broaden CODEOWNERS tosrc/analyzers/**+src/orchestrator.ts+src/shared/scoring.ts, weighed against autonomy. Not solved here — security: add CODEOWNERS human-review gate for autonomous-routine PRs #295 deliberately chose narrow scope.