#How do integrate github (or a selfhost alternative) such that my agent can read issues and make PRs

1 messages · Page 1 of 1 (latest)

nimble smelt
#

I want my agents to tackle issues every heartbeat. I don't want to have to command them directly.

My idea is a workflow where I can post issues, and then the agents go through and create PRs to fix those issues. This can either be through GH or a self-host, my openclaw is already running in a podman compose so I have no problems with selfhost

I want the cron/heartbeat to be everything, simply triggering the agent every time a comment on their PR is posted leads to a necessity to respond. Rather, Id like a notification feed for the agents to read over and solve their PR comments if ther are any else go through other PRs and critique them or find issues to make PRs for

This way, my agents will scrutinize each other according to their strengths. Main coder makes the PR, security agent checks for holes, efficiency agent looks for slowdowns, etc.

Is this easy to acheive in openclaw engine or am i better off using a dedicated coding engine? Self host engines only

wide obsidianBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

nimble smelt
#

@safe grove

safe groveBOT
# nimble smelt <@1457407575476801641>

Right now the simplest way is: give the agent shell access (via exec) to a checked-out repo + the GitHub CLI (gh) + git, and let it do:

  • read issues: gh issue list / gh issue view …
  • create branches/commits: git checkout -b …, git commit …, git push …
  • open PRs: gh pr create …

1) Authenticate safely (least privilege)

Use either:

  • GitHub fine‑grained PAT (recommended): scope it to one repo (or a few), with permissions like Issues: Read, Pull requests: Read/Write, Contents: Read/Write (needed to push branches).
  • or a GitHub App (more work, best for orgs).

Keep the token out of prompts; store it as an env var (e.g. GITHUB_TOKEN) on the machine running OpenClaw.

2) Make exec safe (so the agent can’t run arbitrary commands)

Configure exec to run on the real host with approvals + allowlisting, and allowlist only git + gh.

Docs:

#

Key idea: set tools.exec.host to gateway (or a paired node) and use security=allowlist + ask=on-miss (or always) so you approve anything outside the allowlist.

3) Put the repo where the agent can operate on it

Either:

  • run the agent in a workspace that already contains the repo clone, or
  • have it clone to a known path and then always run exec with workdir set to that repo directory.

Self-hosted alternative

If you want “GitHub but self-hosted”, Forgejo (or Gitea) is the usual pick. Same pattern:

  • use git for code operations
  • use Forgejo/Gitea’s CLI (tea) or API for issues/PRs
  • allowlist git + tea in exec approvals

“First-class” integration (no shelling out)

#

If you don’t want to rely on exec, you can build/enable a plugin that exposes a dedicated tool (e.g. github_issues_list, github_pr_create) that calls the REST API with a constrained permission model.
Docs: https://docs.openclaw.ai/plugin

If you tell me your target (GitHub vs Forgejo) + where OpenClaw runs (Linux/macOS, sandboxed or not), I can suggest a tight allowlist + an example gh workflow for “issue → branch → commit → PR”.

nimble smelt
#

id like to not have to approve everything. id rather the agents just do what they want. i would of course simply prevent them from making destructive decisions like closing PRs (and they certainly couldnt approve them)

Idc if I'm using forgejo or github, as long as it works well. i would only choose forgejo if i can simply add it to my compose file and it just works (obviously some authentication or whatever included). would it be easier to create additional accounts for each agent using forgejo than it would to create bots for each one on github?

also how does forgejo stack up to normal gitea and other alternatives?

safe groveBOT
# nimble smelt id like to not have to approve everything. id rather the agents just do what the...

You can get “agents just do the work” without approving every command — but you’ll want to move the guardrails away from OpenClaw prompts and into (a) exec allowlisting + (b) Git host permissions/branch protections.

1) No-approval OpenClaw setup (still safe-ish)

Don’t use security=full unless you truly mean “can run any shell command on that machine”.

Instead do:

  • Exec approvals: security=allowlist, ask=off
  • Allowlist only the binaries you want (typically git + a forge CLI like gh or tea)
  • Then the agent can run those freely, without prompts, but can’t run rm, curl, docker, etc.

Docs:

Important nuance: allowlisting matches resolved binary paths, not just git by name.

2) “Prevent destructive actions” — do it on the Git server

#

You generally can’t give “open PR but never close PR” permissions via API scopes. So instead:

Hard rules that work well (GitHub or Forgejo):

  • Protect main (and release branches):
  • require PRs
  • require 1–2 reviews
  • require CI checks
  • disallow force-push
  • Ensure the agent token/user:
  • cannot merge (because branch protection requires human review)
  • cannot push to protected branches
  • does not have “admin” repo rights

If you really care about “don’t close PRs”:

  • treat closing as non-catastrophic (reopen is easy), or
  • make PR creation happen via automation (e.g. agent pushes a branch, a workflow opens the PR), so the agent never gets PR write permission at all.
#

3) GitHub vs Forgejo for “one account per agent”

GitHub

There’s no special “bot account” type. You either:

  • create separate normal users (“machine users”) and add them to your org/repos (often counts as seats in org plans), or
  • use a GitHub App (cleaner auth story; still one “identity”, but good permission control)

Forgejo

Forgejo makes “one account per agent” very straightforward:

  • create agent-1, agent-2, …
  • mint a token per account
  • add each to the repo/org team with only the permissions you want

So yes: Forgejo is usually easier for per-agent identities.

4) Forgejo vs Gitea (and others)

  • Forgejo vs Gitea: Forgejo is a community-driven fork; they’re very similar in day-to-day use and broadly compatible. The big differences are governance + licensing direction (Forgejo moved to GPL for the overall project; Gitea is MIT). Feature parity is generally close.
  • GitLab: much heavier, but very feature-rich (CI/CD, policy controls, etc.). If you want “drop into compose and forget”, GitLab is usually overkill.
#
  • Gogs: lighter, but less active compared to Gitea/Forgejo.

If your priority is “compose file + works well”: Forgejo or Gitea are the sweet spot.

5) Minimal Forgejo-in-compose reality check

Forgejo does have an “it runs in docker compose” path. The official image is codeberg.org/forgejo/forgejo:* (so yes, it’s a single service + DB + volumes type of setup). If you also want CI, you’ll likely add a runner later.


If you tell me which direction you prefer:

  1. GitHub + GitHub App (less ops) vs
  2. Forgejo in compose (more control, easier per-agent accounts),

…I can outline the cleanest permission model for “agents can open PRs freely but can never merge / never touch protected branches”, and the recommended OpenClaw exec allowlist for it.

nimble smelt
#

I will probably need CI no? A PR is published, CI checks if it passes linting, build, tests.. immediate feedback for the bug chasing agent

nimble smelt
#

krill read my previous message

safe groveBOT
#

You can build this in OpenClaw, but it’s not “turnkey GitHub issue → PR” out of the box. The pieces you want are there (scheduler + multi-agent + command execution), and you glue them together with either:

  • Polling via gh/API on a schedule (pure cron/heartbeat, no “must respond immediately”), or
  • Webhooks → append to an inbox → cron processes the inbox (still no immediate response unless you want it).

The OpenClaw-native building blocks

A practical architecture for “agents tackle issues and PR comments”

  1. Use a dedicated bot identity + least-privilege token

    • Separate GitHub account or GitHub App.
    • Give it only the repo permissions it needs (read issues, write PRs, comment).
  2. Make sure the Gateway can actually run the tooling

    • Your container needs git + GitHub CLI gh available.
#
  • Authenticate gh inside the environment the Gateway uses.
  1. One “triage” job that builds a queue/feed (quietly)

    • Runs every N minutes.
    • Polls:
      • Issues with labels like agent:todo
      • PRs authored by the bot sorted by updated
      • New review comments since last run
    • Writes a small state file in the workspace (e.g. data/github-inbox.json + data/last-seen.json).
  2. Separate worker jobs per agent role

    • coder: picks one queued issue → creates a branch → pushes → opens a draft PR
    • security: reviews open PRs → leaves review comments
    • perf: looks for perf footguns → comments
    • These can each be their own openclaw cron add --agent <id> jobs.
  3. Don’t auto-merge

#
  • Use protected branches + required checks + required human approvals. Let the agents open PRs + comment, but keep “merge” as an explicit step.

What this looks like with OpenClaw cron

OpenClaw cron can run isolated jobs pinned to an agent:

# Triage poller (no need to announce every run)
openclaw cron add \
  --name "Repo triage" \
  --every "15m" \
  --session isolated \
  --agent triage \
  --message "Poll GitHub for new agent:todo issues + new comments on bot PRs. Update data/github-inbox.json and data/last-seen.json. Do not open PRs in this step." \
  --no-deliver
#
# Coder: create PRs (you might want this to announce a summary somewhere)
openclaw cron add \
  --name "Coder worker" \
  --every "30m" \
  --session isolated \
  --agent coder \
  --message "Read data/github-inbox.json. If there is a PR comment thread needing action, address it. Else pick one agent:todo issue, implement, run tests, push branch, open a DRAFT PR. Record results back into data/last-seen.json." \
  --announce --channel discord --to "channel:YOUR_CHANNEL_ID"

(Flags above are straight from openclaw cron add --help on current builds; you can tune --announce/--no-deliver depending on how noisy you want it.)

GitHub vs self-hosted

  • GitHub: easiest because gh is very capable for issues/PRs.
  • Forgejo/Gitea (self-host): totally doable, but you’ll likely swap gh for tea (or call the REST API directly) and write a small custom skill/script to normalize “issues/PR/comments” into the same inbox format.
#

“Better to use a dedicated coding engine?”

If your main goal is autonomous coding throughput, a dedicated coding agent (OpenHands/SWE-agent/aider-style loop) can be better at the inner “edit/test/fix” cycle. But OpenClaw is very good as the orchestrator (scheduling, multi-agent separation, routing, summaries to chat, nodes, etc.). A solid pattern is:

  • OpenClaw cron job triggers
  • It runs a dedicated coding engine via exec
  • OpenClaw posts the summary + links

Two quick questions so I can suggest a concrete “minimal viable” setup

  1. Which forge are you using right now (GitHub, Forgejo/Gitea, something else)?
  2. Do you want the agents to only open PRs + comment, or also push commits to existing PR branches they didn’t create?
nimble smelt
#

for simplicity im going to start with github. this make the process of developing a workflow with my agent smooth, fine tuning its prompt etc.

then maybe ill go selfhost later

for the sake of tru seperation, im actually pretty keen on the idea of creating a seperate branch just for my bot. that way, whether my repo is simply its own fresh repo, or a fork of another repo, my bot has its own fork with total commit control, and her upstream will always be my repo, and publishing PRs to community repos would always be by my hand (i worry of the bot pushing a bad commit to a totally fine PR)

To break it down:

  • <possible up-upstream> no access
  • <upsteam> my repo
  • <origin> bot repo
  1. bot creates feature branches on its repo
  2. bot pushes a PR to my repo, not to main but to an identically named branch
  3. bot has access to commit status, so between workflows and my comments it can fine-tune the PR
  4. I merge the PR
  5. (if i have upstream) I create a PR for my upstream. if the up-upstream wants changes to the PR i instruct my bot to create another PR targeting my feature branch, so that when im happy with the bots changes I can merge it into my feature branch, thus updating the PR for the up-upstream

Let me know your thoughts

safe groveBOT
#

You can get very close to what you described with OpenClaw heartbeat/cron + a thin “GitHub skill” that shells out to gh. The scheduler part is built-in; the “read issues / open PRs / post reviews” part is not a first‑party GitHub integration yet, it’s essentially “give the agent safe, allowlisted access to gh + a repo checkout” and teach it the workflow.

Docs that matter:

On your fork/branch separation idea

Your “origin(bot) → upstream(you) → up‑upstream(optional)” model is sane, and it matches how people safely run machine users.

One practical tweak: don’t make the bot PR into “an identically named branch” in your repo unless the bot also has write access to your repo (or you pre-create branches). GitHub PRs need an existing base branch.

Safer/easier variants that keep the separation you want:

Variant A (simplest):

#
  • Bot has write on its fork only.
  • Bot opens PRs from fork → your repo main
  • Your repo has branch protection + required checks + required review, and bot cannot merge.

Variant B (staging branch):

  • You create a permanent base branch like bot-inbox in your repo (once).
  • Bot opens PRs fork → your repo bot-inbox.
  • You merge from bot-inbox → main when happy.

Either way, you still get: bot can iterate on its PR branch, you control merges, and you never accidentally “publish to community repos” without touching it yourself.

How to make the “notification feed” heartbeat-driven

What you want (“don’t trigger on every PR comment; just have agents poll and decide”) maps really well to heartbeat:

  1. Heartbeat runs every N minutes.
  2. It polls GitHub for “things changed since last time” (notifications, updated PRs, review comments).
  3. If nothing actionable → reply HEARTBEAT_OK (no spam).
#
  1. If actionable → either:
    • do a small action (comment / label / assign), or
    • kick off a deeper isolated run (cron isolated or sub-agent) to actually code.

A decent HEARTBEAT.md starter checklist for this looks like:

# GitHub loop (every heartbeat)

- Pull GitHub notifications / updated PR threads for repos X/Y.
- If any PR review comments are addressed to the bot, pick ONE and prepare a fix.
- Else, find one issue with label "bot:todo" and propose/implement a fix.
- Else, pick one open bot PR and do a quick self-review (tests, lint, obvious security footguns).
- If nothing is actionable, reply HEARTBEAT_OK.

(“Pick ONE” matters; otherwise a single heartbeat tries to boil the ocean.)

#

What you need to wire up (minimum viable)

  • gh available where the agent can execute commands (gateway host or the sandbox container image).
  • A GitHub token for the bot account (fine-grained PAT recommended).
  • Exec approvals set to allowlist and only allow what you need (gh, git, maybe your test runner). See exec approvals doc above.

Then write a small skill that basically teaches:

  • how to list queue items (gh api notifications, gh issue list ..., gh pr list ...)
  • how to checkout/update a worktree
  • how to push a branch to the bot fork
  • how to open/update a PR (gh pr create, gh pr comment, gh pr checks)

OpenClaw is good at being the orchestrator + scheduler + “brain” here; the GitHub operations are just CLI/API plumbing.

Multi-agent “scrutinize each other”

Totally doable, but I’d implement it as:

  • one “manager” heartbeat that triages + assigns,
  • then spawn specialized agents/sub-agents to produce review comments (not commits),
#
  • only the “main coder” agent actually pushes commits.

This avoids multiple agents force-pushing over each other.

If you answer two quick things, I can suggest a tighter “first build”:

  1. Is your OpenClaw running with sandboxing on for the coding agent (container), or will it run gh/git directly on the host?
  2. Do you want the bot to do real code changes automatically, or only open draft PRs + ask before pushing?
nimble smelt
#

I want to pivot the conversation a bit, because you seem pretty focused on general bot configuration/prompting, which is something im pretty confident in figuring out later, and intend to experiment on.

So how it uses its tools, how it splits its agents, how it's cronjobs work.. lets leave that aside, I think we've covered that enough

What i need now is the true implementation of github permissions, and how to split the repos up

heres a scenario id like for you to consider, and its why ive thought about giving the bot its own repo:

lets assume the bot simply has access to my repo, and ive locked it out of touching main

  1. I fork appyfoo (some random repo name for arguments sake)
  2. I create an issue for a bugfix
  3. bot wakes up for its appyfoo cronjob and reads issues, decides to tackle the bugfix
  4. bot makes the changes locally and pushes to a new branch bot-bugfix
  5. i say this looks good, i open a PR to upstream on that branch
  6. for whatever reason, either because the maintainer said they didnt like it, or because it was just tripping, the bot decides to make another push to the branch. Now this isnt necesarrily terrible, thats the whole point of a PR, to work on it. but now its affecting a real PR to a community project with code that I havent looked over yet and that reflects on me
  7. PR gets denied for being sloppy

okay, so there must be a degree of seperation between the bot publishing to the repo, and the repo publishing to upstream

this is why i thought maybe the bot just has its OWN repo

well here is a problem with that

  1. i fork appyfoo
  2. i fork me/appyfoo into me/bot-appyfoo and give bot access
  3. bot creates bugfix and publishes a PR to my repo
  4. I want to now publish those changes upstream, but the PR that bot published from bot-appyfoo to me/appyfoo are now on main. this makes opening a PR and especially updating a PR a little tricky. I want the bot to be able to update their PR as a buffer such that I can update the real PR

thoughts?

safe groveBOT
#

Your instinct is right: never make the “real upstream PR” depend on a branch the bot can keep pushing to. The clean solution is: bot branches live only in the bot fork; upstream PR branches live only in your fork. The bot only ever submits patches to you (via PRs), and you decide when those patches get forwarded upstream.

First: small correction about “the bot PR is now on main”

If the bot opens a PR to me/appyfoo:main, that does not put code on main. The commits live on the PR’s head branch (in the bot repo). main only changes when you merge.

So the “bot PR base is main” isn’t inherently the danger.

The real danger is when the PR you opened to the community upstream uses a head branch the bot can still mutate (force-push / extra commits) after you’ve reviewed.

Recommended repo + permission topology (works great)

Repos

  • appyfoo/appyfoo = community upstream (bot has zero access)
  • me/appyfoo = your fork (bot has read, or “pull requests: write” only)
  • bot/appyfoo = bot fork (bot has contents write)

Bot token permissions (GitHub fine-grained PAT)

  • On bot/appyfoo: Contents: Read+Write, Pull requests: R/W, Issues: R/W (whatever you need)
#
  • On me/appyfoo: ideally Pull requests: Read+Write, Issues: Read (no contents write)
  • On appyfoo/appyfoo: no access at all

This alone prevents “bot accidentally updates a PR to upstream”, because it literally can’t push to the branch you’ll use for the upstream PR.

The “buffer” branch pattern (solves your step 6/7)

Use two layers of PRs:

Layer 1 (bot → you)

Bot opens PR:

  • head: bot/appyfoo:bot-bugfix-123
  • base: me/appyfoo:bot-inbox (or main if you don’t mind, but bot-inbox keeps things tidy)

You review/iterate here. Bot can push as much as it wants: it only affects this PR.

Layer 2 (you → upstream)

When you’re happy, you create a branch in your fork that the bot cannot push to:

#
  • me/appyfoo:upstream-bugfix-123

You update that branch only by pulling in the bot PR in a controlled way:

  • cherry-pick the bot commits, or
  • squash merge the bot PR into upstream-bugfix-123 (via GitHub UI or locally)

Then you open the real PR:

  • head: me/appyfoo:upstream-bugfix-123
  • base: appyfoo/appyfoo:main

Now if the upstream maintainer asks for changes:

  • you ask the bot to update its PR (Layer 1),
  • then you cherry-pick/squash those new commits into upstream-bugfix-123,
  • which updates the upstream PR — but only because you forwarded it.

That gives you exactly the “bot buffer” you want.

#

Extra guardrail (optional but nice)

On me/appyfoo, add branch protection like:

  • protect upstream-* (or whatever prefix you choose)
  • disable force-push
  • require your review / status checks

Even if you later decide to give the bot some write access to me/appyfoo, you can still keep the upstream-facing branches protected from it.

Why “bot has its own repo” is still the right idea

Because it lets you enforce this invariant:

Anything the bot can mutate is never the head of a public-facing PR.

That’s the whole game.

If you tell me whether me/appyfoo is private (vs public), I can be more specific about the minimum permissions the bot needs to open PRs into it without being able to push code there.

nimble smelt
#

hold on i can use wildcards for permissions? please read the documentation before answring to be sure. if this is true this could mean that I can simply keep the bot off main and off my upstream branches and it wouldnt even necesarrily need to make PRs, I could just merge changes into upstrea-*

now that being said, I'm not ruling out the more robust two-repo system I'm just suggesting that this is a pssibility.

If the opposite is available, the ability to sspecifically grant write and creation access only to branches that begin with bot-*, then we could be cooking

safe groveBOT
#

GitHub has wildcards for branch rules (branch protection / the newer Rulesets), but not “branch-scoped repo permissions” in the sense of “this token can only write bot-*”.

So: you can’t give a PAT “write access only to branches matching bot-*”. Permissions are repo-level.
But you can make GitHub reject pushes / branch creation except for certain patterns, which effectively gives you what you want.

The relevant GitHub docs (confirmed)

“Bot can only create/push bot-* branches” (single-repo approach)

In your repo (me/appyfoo), create a branch ruleset like:

Ruleset A (lock everything except bot branches)

  • Target branches: include *
  • Exclude: bot-* (and maybe bot/** if you prefer slash namespaces)
#
  • Enable:
    • Restrict creations
    • Restrict updates
  • Bypass list: only you (practically: “repository admins” with only you as admin; do not make the bot an admin)

Effect: the bot account can have “write” at the repo level, but GitHub will block it from creating/pushing any branch except bot-*.

Ruleset B (protect your important branches)

  • Target: main, upstream-*
  • Enable the usual stuff: “require PR”, required checks, no force-push, etc.

This gets you very close to “branch permissions with wildcards”.

Important caveats

  • Bypass list in rulesets is roles/teams/apps (and admins), not always “pick an individual user” unless you’re in an org with teams. So the simplest safe setup is: only you are admin, bot is write.
  • Rulesets availability depends on plan/repo visibility (GitHub docs note: public repos get rulesets on Free; private repos may require paid plans).
#

Does this remove the need for the two-repo system?

It can, if your goal is strictly: “bot can push code, but only into bot-prefixed branches, never into main/upstream-*”.

But it doesn’t solve the “bot accidentally updates the head branch of a community PR” unless your upstream PR’s head branch is also protected from the bot (which it will be if you use upstream-* and block bot pushes there).

If you want, paste whether me/appyfoo is a personal repo or org repo (org gives you teams, which makes bypass lists cleaner), and whether it’s public or private (plan constraints). Then I can suggest the exact two rulesets/patterns that minimize pain for your own workflow while still hard-blocking the bot.

nimble smelt
#

youve aksed many times: yes this would be public. private repos are a whole different ballgame because of the monetary restrictions

also i wasnt qware of slash notation, i like that

so i can use wildcards to create restrictions, but you seem to imply its not so easy to give a PAT specifically "you may only write to bot/** but you can read everything else"? Id have to use some sort of cheeky workaround? could I not finetune that with a github app... or by creating another github account just for the bot and giving the user a role.. or anything?

I'd like to not have to create these big blanket rules that would negatively affect other contributors

safe groveBOT
#

You can use wildcards for branch rules (rulesets / branch protection patterns), yes. But you cannot scope a PAT itself to “write only to branches matching bot/**”.

1) PAT / fine‑grained PAT: repo‑scoped, not branch‑scoped

Even a fine‑grained PAT is basically:

  • choose which repos it applies to
  • choose permission buckets (Contents: read/write, PRs, Issues, etc.)

There’s no “this token can only push to branches matching X”. So you always need server-side enforcement (rulesets / branch protections) if you want branch-level safety.

2) Can a GitHub App do branch‑scoped write?

Same story: a GitHub App’s installation token is still repo-scoped. The way you get branch scoping is again rules on the repo (rulesets / protected branches) that treat the App as an “actor”.

Where Apps help a lot: rulesets bypass lists include GitHub Apps (not just human roles), so you can craft “only this App can update bot/* branches”.

Docs:

#

3) The clean “bot/** only” setup that doesn’t hurt contributors

In an open-source public repo, almost nobody has push anyway. “Other contributors” typically fork+PR, so branch push restrictions don’t affect them.

What it does affect is co-maintainers with write access. So keep write access minimal and use a team/org if you have multiple maintainers.

Recommended pattern (minimal blast radius):

  • Put bot auth behind a GitHub App (or a dedicated bot user, but App is nicer here)
  • Create a ruleset targeting only bot branches:
    • Target: bot/* (one segment) and/or bot/**/* (nested). Note: * doesn’t match /.
    • Turn on:
      • Restrict creations
      • Restrict updates
    • Bypass list: the bot GitHub App (and optionally “repository admins” so you can still fix things)
#

Result: bot can only create/push bot/... branches; it can still read everything else; humans can continue normal workflows elsewhere.

Then separately protect:

  • main, release/*, upstream-* with your usual “require PR, checks, no force push” stuff.

4) One important nuance: personal repo vs org repo

If you want the older “Restrict who can push to matching branches” (where you pick specific people/apps), GitHub’s docs call out it’s an org-owned repo feature in some plan combinations (it’s not universally available on personal repos). From the branch protection docs:
https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule (see the note about branch restrictions being for org repos)

Rulesets are generally the more flexible modern mechanism anyway.

Bottom line

  • No, you can’t make a PAT that is intrinsically “bot/** write only”.
  • Yes, you can achieve the effect safely with rulesets targeting bot/* + bypass for a GitHub App, and it won’t mess with normal contributors (only with people who already have push access).

If you tell me one thing—is the repo under a personal account or a GitHub org?—I can suggest the exact “ruleset A / ruleset B” layout that matches what GitHub will actually let you configure in that case.

nimble smelt
#

its just a personal account

nimble smelt
#

@safe grove read my last message i answered your question

safe groveBOT
#

You answered public (got it). The thing I still need to know is: is me/appyfoo under a personal account or a GitHub org? That changes what “restrict who can push” options you’ll see.

Also: please don’t @mention/ping me in here (Discord policy on my side) — I’ll still reply.

Can a PAT be “write only to bot/**”?

No. PATs (classic or fine‑grained) are repo-scoped, not branch-scoped. You can pick:

  • which repos the token applies to
  • which permission buckets (Contents/PRs/Issues/etc)

…but there’s no “this token may only push to branches matching bot/**”.

So if you want branch-level control, you enforce it with server-side rules (Rulesets / branch protections), not the token itself.

Can you do it cleanly without harming contributors?

Yes, if you think about who actually pushes:

  • Normal OSS contributors don’t push to your repo anyway (they fork + PR), so branch push restrictions usually don’t affect them at all.
#
  • The only people affected are collaborators with write who currently push branches directly.

Best “single repo” setup (no second fork required)

Use Rulesets with patterns (fnmatch wildcards) and restrict pushes/branch creation:

  1. Global ruleset (blocks the bot from pushing anywhere except bot branches)
  • Target branches: include *
  • Exclude: bot/** (and/or bot/* depending on how you name)
  • Turn on:
    • Restrict updates
    • (optionally) Restrict creations
  • Bypass: your maintainers (details depend on personal vs org), NOT the bot
  1. Bot ruleset (allows only bot actor to push bot branches)
  • Target branches: bot/**
  • Turn on:
    • Restrict updates
#
  • Restrict creations
  • Bypass: only the bot actor (ideally a GitHub App; second-best a bot user)

This yields the effect you want: bot can only push bot/**, can read everything else.

GitHub App vs separate bot account

  • GitHub App is the cleanest actor to put on bypass lists (rulesets explicitly support “GitHub Apps” as bypass actors).
  • A separate bot user + fine-grained PAT can work too, but depending on whether you’re personal vs org you may have less precise actor controls.

Why I’m asking “personal vs org”

Some “restrict who can push to matching branches” UX is org-only in certain plan combos, and org repos give you teams to use as bypass actors. Personal repos are usually more limited in “pick exact people” style controls.

Reply with: personal or org, and whether you have any other maintainers with write who need to push branches, and I’ll tell you the exact ruleset strategy that minimizes collateral.

nimble smelt
safe groveBOT
#

Got it — personal account, public repo. I missed that in your earlier note; sorry about the mismatch. Also: please don’t ping me; I’ll still see the thread.

Key point (still true): tokens can’t be branch-scoped

Even on a personal repo, PATs (classic or fine‑grained) can’t be limited to “write only bot/**”. Branch scoping has to be enforced by repo rules (rulesets / branch protection), not by the token.

The “no collateral damage” way on a personal public repo

Because outside contributors generally don’t have push anyway, a ruleset that restricts pushes typically doesn’t affect them (they fork + PR). The only people it can “hurt” are collaborators you’ve granted write to.

So the clean setup is:

Option A (recommended): GitHub App for the bot (not a bot user)

Use a GitHub App as the bot identity (so you can treat it as a distinct “actor” without lumping it into “all write collaborators”).

Then create two rulesets:

  1. Bot branches ruleset
  • Target: bot/** (and/or bot/*)
#
  • Enable: Restrict creations + Restrict updates
  • Bypass: the GitHub App (and you/admin)
  1. Everything else ruleset
  • Target: *
  • Exclude: bot/**
  • Enable: Restrict creations/updates only if you truly want to block all direct pushes except by you
    (Many people skip this and just protect main/release/* instead.)

This gives you the effect you want: the bot can push only to bot/**, while humans can continue normal PR workflows.

Docs for patterns + restrict rules:

Option B (works but “blanket-y”): bot user as collaborator

If the bot is a separate GitHub user and you grant it write, then any rule that bypasses “write role” will also bypass for the bot user — which ruins the branch scoping. That’s why I’m steering you toward a GitHub App for this.

#

Practical recommendation for your goal

For a personal public OSS repo: protect main hard, and allow the bot only on bot/** via rulesets + GitHub App actor. That won’t negatively affect drive-by contributors because they weren’t pushing branches in your repo anyway.

If you want, tell me: do you currently have any other collaborators with direct write (not just PRs from forks)? If yes, I’ll suggest the least-annoying ruleset layout so they can still work normally without accidentally giving the bot extra reach.

nimble smelt
#

Ok, github app. What perms do i give it. check the docs for up to date perms, what is every perm i should provide to do this?

safe groveBOT
#

For a GitHub App that will read issues + create commits/branches + open/update PRs + comment, the core permission is Contents. GitHub’s docs explicitly call out that HTTP git access with an installation token requires Contents (and Workflows only if you need to touch .github/workflows).
Source: https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app#choosing-permissions-for-git-access

Minimal GitHub App permissions (repo-scoped) for your bot workflow

Repository permissions

Set these on the App:

  • Contents: Read & write
    Needed to git push branches / create commits.

  • Pull requests: Read & write
    Needed to open PRs, update PRs, and comment/review on PRs.

  • Issues: Read & write (or Read-only if you don’t want it filing/editing issues)
    Needed to read issues, post comments, add labels/milestones, etc.

#
  • Metadata: Read-only
    This is effectively required for most repo interactions (GitHub treats it as a baseline permission).

Strongly recommended “read-only observability” (optional but very useful)

These let the bot see whether CI passed without giving it power to change CI:

  • Checks: Read-only (to read check runs / check suites)
  • Commit statuses: Read-only (to read combined status on a ref)

Permissions I recommend not granting (unless you’re sure)

  • Workflows (lets it edit workflow files; big supply-chain footgun for a coding bot)
  • Administration
  • Secrets / Variables / Environments-type permissions (anything that can exfiltrate creds)
  • Actions: write (can dispatch/rerun workflows; sometimes okay, but start without)

Install configuration (most important safety lever)

When you install the App on your personal account, choose:

#
  • Only select repositories → pick only the bot-managed repos (not “all repositories”).

That gives you repo-level containment even before branch rulesets.

If you want this “perfectly minimal”

GitHub will 403 an API call if you’re missing something, and you can iterate. The REST API responses can include an X-Accepted-GitHub-Permissions header telling you what permission is required (docs mention this on the same page under REST API access).

If you tell me whether you plan to:

  1. use pure git push + gh pr create, or
  2. use REST API for creating commits/branches directly,
    …I can trim this to the absolute minimum set for your exact implementation.
nimble smelt
#

What rulesets exactly do i need to make krill

nimble smelt
#

@safe grove i setup the github apl and gave jt the permissions, installed it to my repo, created an exclusionary to "require PR" on every part of the repo except bot/**/*

now how do i get the local side setup such that my bot can actually do work?

safe groveBOT
#

You’ve already done the GitHub-side (App + permissions + branch protection). The “local side” is basically: (1) give the agent a GitHub credential it can use from the box/container, and (2) make sure the box/container has git + gh + a writable checkout, and (3) trigger it via heartbeat/cron with a predictable queue (labels/search).

0) First: what version are you on?

Please paste:

openclaw --version

(Version mismatches change what config knobs / CLI flags exist.)


1) Give the bot GitHub auth (practical recommendation)

Even if you created a GitHub App, the simplest way to unblock yourself is:

Option A (recommended to start): Fine‑grained PAT for a dedicated bot user

  1. Create a separate GitHub user for the bot (recommended by the FAQ).
  2. Create a fine-grained PAT with repo access + permissions to:
#
  • Issues (read)
    • Pull requests (read/write)
    • Contents (read/write) (needed to push branches)
  1. Inject it into your podman compose as an env var:
    • GH_TOKEN=... (what gh uses)
    • (optional) also GITHUB_TOKEN=... for some tooling

Then gh will “just work” without any interactive login.

Option B (your GitHub App): you must mint installation tokens

GitHub App tokens are short-lived and you need a small “token minter” step that periodically creates an installation access token and exports it as GH_TOKEN before running gh .... Totally doable, just more plumbing than Option A.

(If you want, tell me if you’re set on App auth and I’ll give you a minimal token-minter script/sidecar approach for podman.)


2) Make sure your OpenClaw runtime can actually run git/GitHub commands

#

Inside the same environment where the Gateway runs (your container):

  • git installed
  • gh (GitHub CLI) installed
  • a persistent writable directory mounted (so checkouts + caches survive restarts)

Sanity checks from inside the container:

gh --version
git --version
gh auth status
gh repo view OWNER/REPO

OpenClaw doesn’t magically use the GitHub App you created — it will use whatever credentials are available to the commands it runs (typically via GH_TOKEN).


#

3) Decide how the agent finds work (queue pattern)

Heartbeat/cron needs a deterministic “inbox” so it doesn’t thrash:

Common pattern:

  • Issues labeled bot:todo
  • PRs labeled bot:needs-fix
  • PR review comments mentioning a keyword like bot pls fix (and you search for it)

Example queries the agent can run:

gh issue list --repo OWNER/REPO --label "bot:todo"
gh pr list --repo OWNER/REPO --label "bot:needs-fix"

4) Trigger it: heartbeat (batched) or cron (isolated)

#

You said you want this to be heartbeat/cron-driven (not “must respond immediately”).

Heartbeat approach (batched, low-noise)

Put a tiny checklist into HEARTBEAT.md in the agent workspace (this is exactly what heartbeats are for):

Cron approach (clean isolation, easy per-agent scheduling)

Make an isolated cron that runs every N minutes and does a sweep:

You can also pin different crons to different agents (coder vs security reviewer etc).


5) Multi-agent “coder + reviewers”

This part is supported: you run multiple isolated agents and schedule them separately.

#

Typical setup:

  • agent coder: creates branches + PRs
  • agent security: reviews open PRs and leaves review comments
  • agent perf: scans for obvious perf footguns

If you answer these 3, I can give you a concrete minimal config + exact cron/heartbeat prompts

  1. openclaw --version output
  2. Are you okay using a PAT (fast path) or do you insist on GitHub App tokens?
  3. Repo host is GitHub.com, right? And do you want the bot to only touch issues with a specific label (recommended)?
nimble smelt
#

ok its working. heres the thing. i have a rulset to block creations, merges, updates, and "must submit pr before merge" for all branches except for esther/**/* (bots name), applies to everyone except admin and maintain

but i get this ugly red "merge is blocked, cannot update protected ref" and i need to chck a "bypass and merge"

am i doing rulesets right? is there a cleaner way to do this that doesnt leave a gross blocking warning on every PR just because i wanna keep my bot off main?

or does that involve the whole two-repo solution

safe groveBOT
#

That red banner is basically GitHub telling you: “this action would update a protected ref (a merge is a push to the base branch), and the ruleset is currently set up to block that update, so you’re only allowed via bypass.”

So yeah: if you need to click “bypass and merge” on every PR, your ruleset is doing something too broad/too strict.

What to change (clean setup, no constant bypass warnings)

Protect only the base branches (usually main, maybe release/*) and don’t use “block updates/merges” style rules there.

For main you typically want:

  • Require PR before merge
  • Require status checks
  • (Optional) require reviews, linear history, etc.
  • Do NOT “Restrict/Block updates” unless you truly want to forbid all pushes (because merges count as pushes)

Then your bot safety goal (“bot can’t touch main directly”) is handled by:

  • making sure the bot account is not admin/maintainer
  • and/or “restrict who can push” to main (so only merges via PR happen, no direct pushes)
#

About your esther/**/* exemption

If the bot is still tripping protections, either:

  • the exemption pattern isn’t actually matching your branch names the way you think, or
  • you’ve got another ruleset also applying (rulesets stack; the restrictive one wins)

As a sanity check, keep bot branches simple like esther/foo and use a simple pattern like esther/* (and/or esther/** if GitHub accepts it in that UI) rather than getting fancy.

Can you hide the warning while still bypassing?

Not really — the warning is the point/audit trail. The “clean” look comes from not needing bypass in the first place.

Do you need the 2‑repo (fork) solution?

Only if you want a hard security boundary where the bot doesn’t have write access to the main repo at all. For “bot shouldn’t push to main”, a single repo + correct branch rules is the normal way.

If you paste (screenshot/text) the exact rules you enabled in the ruleset (especially anything like “restrict updates/merges”), I can tell you exactly which checkbox is causing the bypass-only merges.

nimble smelt
#

You're giving me too much. lets laser in on rulesets. give me a ruleset to try and ill tell you how it goes

safe groveBOT
#

Try this single ruleset first (and temporarily disable your other rulesets while testing so you can see the effect clearly):

Ruleset: Protect main (PR-only)

Target: main only

  • Branch name pattern: main (no excludes)

Rules (turn ON):

  • Require a pull request before merging
  • Restrict deletions
  • Restrict force pushes
  • Restrict who can push to matching branches → allow only Admins/Maintainers (or whatever role you use to merge)

Rules (leave OFF):

  • Anything like “Block updates” / “Restrict updates” / “Block merges” / “must bypass” style restrictions

Bypass list:

  • Leave empty (or keep it to true break-glass admins). You shouldn’t need bypass for normal PR merges with the rules above.
#

Expected result

  • Bot can push to esther/* branches and open PRs into main.
  • PRs merge cleanly without the ugly “cannot update protected ref” bypass banner (because merges to main are allowed via PR for maintainers).
  • Bot cannot push directly to main.

If that works, re-enable your other rulesets one-by-one; the moment the red banner returns, the last one you enabled is the culprit.

nimble smelt
#

if i have restrict updates off, wouldnt esther be able to push straight to main?

safe groveBOT
#

No — not if you have “Require a pull request before merging” enabled on main.

Here’s the key distinction in GitHub rulesets:

  • Require a pull request before merging
    = “All changes to this branch must come from a PR.”
    So a direct git push origin main (from Esther or anyone) gets blocked.

  • Restrict updates
    = “Only bypass actors can push/update this ref at all.”
    A PR merge is also an update to main, so enabling this is what creates the constant “cannot update protected ref / bypass and merge” experience.

So the clean setup is:

  • On main: Require PR before merging = ON, Restrict updates = OFF
  • Make sure Esther is not in the ruleset bypass list (and isn’t admin/maintainer)

If Esther can still push to main directly with “Require PR before merging” on, then either:

#
  1. that rule isn’t actually applying to main (pattern mismatch / another ruleset overriding), or
  2. Esther is included as a bypass actor (common if you added the GitHub App to bypass).
nimble smelt
#

can esther approve PRs

safe groveBOT
#

Not if Esther is the author of the PR.

GitHub blocks self-approval: “Pull request authors cannot approve their own pull requests.” (GitHub docs)

What that means in practice:

  • If Esther (same GitHub identity: user/app/token) opened the PR, then gh pr review --approve as Esther won’t work / won’t count.
  • If you want “bot approves bot PR”, you need a second GitHub identity (separate bot user or separate GitHub App installation token) to act as the reviewer (e.g. your security agent).

If your goal is just automation (no humans), the cleaner pattern is usually: no required approvals, rely on required status checks + auto-merge; let bots leave comments, not approvals.

nimble smelt
#

no i want to be the final PR approval. that does mean that esther will be able to approve other prs bc of the write access tho?

safe groveBOT
#

Yes. If Esther has write access, Esther can generally submit PR reviews (including “Approve”) on other people’s PRs.

But you can still make only your approval matter:

Make “only me can approve” (practically)

On the main ruleset, enable:

  • Require a pull request before merging
  • Require pull request reviews (set approvals = 1)
  • Require review from Code Owners

Then add a CODEOWNERS file that makes you the code owner for everything, e.g.

  • * <your-user-or-your-1-person-team>

Result:

  • Esther may still click “Approve” on other PRs, but it won’t satisfy the “code owner review required” gate unless Esther is also a code owner.
  • You become the final approval chokepoint.
#

If you want, paste your current ruleset checkboxes and I’ll tell you exactly which ones to flip to get this behavior without reintroducing the “bypass/protected ref” warning.

nimble smelt
#

it really almost seems like the best option is actually to split off that repo so that she can just have free reign over her own doesnt it.. i could lock down main so that its impossible for her to touch it, only sync, and in her cron specify that her objective for each branch is to
sync main
make a branch (or rebase a working branch)
commit
PR to upsteam (me)
rinse and repeat

would almost be simpler at this point considering how bianry githubs permissions are? i feel like even discord has more finetune control over how roles work tbh lol

safe groveBOT
#

Yeah, if your goal is “Esther has zero ability to touch main, ever” and you don’t want to constantly reason about GitHub’s bypass/roles edge-cases, the two-repo model is genuinely simpler and more robust.

The cleanest mental model (2‑repo)

  • Upstream (yours): protected main, only you (and CI) can merge. Esther has no write access here (or only very minimal like read + PR via fork).
  • Esther fork (hers): she has full write/admin. She can do whatever: branches, force-push, experiments.
  • Workflow:
    1. Sync fork’s main from upstream main
    2. Create/update a working branch
    3. Push to fork
    4. Open PR from fork → upstream
    5. You review + approve + merge upstream
    6. Repeat

This gives you an actual permission boundary instead of “ruleset gymnastics”.

Why it’s simpler than rulesets for bots

GitHub branch rules are great for humans, but for bots they’re blunt:

#
  • “write” is pretty powerful
  • bypass lists / maintainer/admin carveouts create weird UI states
  • merges are “updates to protected ref”, so you get confusing banners if you over-restrict

Fork model avoids all that: Esther can’t “accidentally” push to upstream main because she literally doesn’t have the capability.

One gotcha to be aware of

If your upstream repo is private, forks can be trickier depending on your org plan/settings. If that’s your case, the alternative is the same idea but with a separate repo that Esther owns (not a fork) and you cherry-pick/PR via patches—less nice, still a hard boundary.

If you tell me whether the repo is in a personal account vs org, and public vs private, I can tell you which variant (fork vs separate repo) is the least painful.

nimble smelt
#

so assuming then that i limit her github app's installation to just her fork, would she still be able to request PRs upstream (to my repo)? or would she need to be installed to both? you dont typically need to be part of a repo to make a pr right

safe groveBOT
#

If Esther is authenticating as a GitHub App installation token, and you install that App only on her fork, then:

  • She can push branches to her fork âś…
  • She cannot create / comment on / label PRs in your upstream repo via API/gh ❌
    Because POST /repos/<you>/<upstream>/pulls is an upstream-repo API call, and an installation token only has access to repos the App is installed on.

“You don’t need to be part of a repo to make a PR” is true for human GitHub users on public repos (fork → PR), but a GitHub App token isn’t “a random internet user” — it’s scoped to installed repos.

So your options are:

  1. Install the App on both repos (fork + upstream) so it can open PRs upstream.
    Caveat: GitHub App permissions are app-wide, not per-repo, so if the App has broad perms, that broadness applies anywhere it’s installed.

  2. Keep the App installed only on the fork, and use a different credential for upstream PR creation (e.g. a bot user PAT with just PR permissions), or open the PR manually via the compare URL.

If you tell me whether the upstream repo is public or private, I can tell you what the minimum needed is (private changes the “anyone can PR” assumption a lot).

nimble smelt
#

aw geez, so that makes it just as complicated huh

just assume everything is public btw

safe groveBOT
#

If everything is public, it’s less complicated — but the punchline stays:

GitHub App token: still needs upstream install to open PRs upstream

A PR is an object in the upstream repo. Creating it via API/gh requires auth that has access to that upstream repo.

A GitHub App installation token only has access to repos it’s installed on, so if you install the App only on Esther’s fork, she can push/code there, but she can’t create the PR in your upstream repo.

So with a GitHub App, you’re basically stuck with “installed on both” if you want the bot to open PRs automatically.


The simpler public-repo solution: use a bot user + PAT (no App)

This is the classic “fork workflow”:

  • Create a GitHub user for Esther
  • Esther forks your upstream repo (public)
  • Give Esther a fine-grained PAT for her fork (repo contents read/write on the fork)
#
  • Esther pushes branches to her fork
  • Esther opens PRs to your upstream as a normal GitHub user

A normal user can open PRs to a public repo without being a collaborator. (They won’t be able to push to upstream branches or merge unless you grant that.)

This ends up cleaner than App scoping because:

  • Upstream stays locked down (Esther not a collaborator)
  • Esther still can PR in
  • No “App installed on upstream means it can do upstream things” worry

If you want, I’ll give you the exact gh pr create ... command format for “fork → upstream” once you tell me: upstream owner/repo and fork owner name.

nimble smelt
#

wait so youre suggesting just doing away with the whole github app thing and just creating a second repo where esther has a PAT? does she need a whole ass user or does a deploy key work? (i assume deploy key is what you mean by PAT?)

safe groveBOT
#

PAT ≠ deploy key.

Deploy key (SSH key)

  • Lets you git clone/push to one specific repo over SSH.
  • Cannot use the GitHub API as an identity → it can’t reliably:
    • read issues via API
    • create PRs
    • comment/review
    • approve
      So a deploy key alone won’t give you the “agent reads issues + opens PRs” workflow.

PAT (token)

  • A Personal Access Token is an API token tied to a GitHub user (ideally a dedicated “machine user” like esther-bot).
  • This is what gh uses via GH_TOKEN, and it can:
    • list issues
    • create PRs
    • comment/review, etc.
#

Do you need a whole user?

If you want the bot to open PRs automatically, the cleanest setup is: yes, make a dedicated GitHub user for Esther and give it a fine‑grained PAT with minimal permissions.

If you want a good security boundary:

  • Use deploy key or token for pushing code to Esther’s fork
  • Use a PAT with only “Pull requests: read/write” (and maybe Issues) for creating PRs upstream
  • Do not give “Contents: write” on the upstream repo (so it can’t push to upstream branches)

If you want, tell me which you’re doing for git pushes (HTTPS w/ token vs SSH deploy key), and I’ll give you the minimal permission checklist for the PAT.

nimble smelt
#

ok, so to simplify, i create a bot user (like literally a normal github account but for esther?), authorize her with a PAT, and then i just fork my repo on her account and she can use that willy nilly and push PRs upstream?

safe groveBOT
#

Yes — that’s the simplest “hard boundary” setup for a public repo.

Recipe

  1. Create a dedicated GitHub account (machine user) for Esther.
  2. On that account, fork your repo.
  3. Create a fine‑grained PAT on Esther’s account.
    • Give it access to the fork repo (read/write contents) so she can push branches.
    • (Optional) also allow Issues/PR read if you want her to query via API.
  4. Configure OpenClaw/container with GH_TOKEN=<esther_pat> (or whatever credential you’re using).
  5. Esther pushes branches to her fork, then opens PRs from fork → your upstream.

What this buys you

  • Esther does not need write access to your upstream repo.
  • You can lock upstream main down hard (rulesets/required reviews/etc.).
  • PRs still work normally because PRs from forks are first-class on public repos.

Only caveat: if your PR workflow needs actions that require “write” on upstream (labels, assigning reviewers, auto-merge toggles, etc.), those might be limited unless you grant extra permissions — but for “open PR + you approve,” it’s perfect.

nimble smelt
#

can i make it so that her PAT prevents her from updating main but she can still sync it? or at this point should i just let her haev total control over her repo since its hers

#

@safe grove read my last message

safe groveBOT
#

Yep — you’ve got it.

For a public repo, the simplest setup is:

  1. Create a normal GitHub account for Esther (machine user).
  2. Fork your repo under Esther’s account.
  3. Create a fine‑grained PAT on Esther’s account (scoped to the fork), and set it in your runtime as GH_TOKEN.
  4. Esther works only in her fork: create branches, push commits, then open PRs from the fork → your upstream.
  5. You do the final review/merge on upstream.

That keeps upstream locked down without ruleset/bypass weirdness, because Esther never has upstream write perms in the first place.

nimble smelt
#

if i want esther to be able to fork other projects should i give her a classic token?

safe groveBOT
#

If you want Esther to be able to programmatically contribute to arbitrary public repos she doesn’t own / isn’t a collaborator on (create PRs, comment, etc.), then yeah: you’ll likely need a classic PAT.

GitHub’s own docs call this out as a current limitation of fine‑grained PATs: classic PATs are the ones that have write access for public repositories not owned by you / org you’re a member of.

Practical recommendation (since you said “assume everything is public”):

  • Keep a dedicated bot user.
  • Use a classic PAT with the smallest scope that works:
    • usually public_repo is enough for public repos
    • add more only if you hit a concrete permission error

If you keep Esther only working inside repos owned by the Esther account (her forks) and you don’t need her to write PR objects upstream via API, then fine‑grained can be okay — but the moment you want “work on random upstreams” smoothly, classic is the path of least pain.

nimble smelt
#

I made the github account for her. I realize that fine grained PAT can be set to "all repos" aka all owned repos, which is great, she should have the same perms across all her repos.

for PAT perms i was thikning rw for:
content
issues
prs
commit statuses
actions

lmk if theres anything i should add or remove?

the big caveat here is now that you're saying fine grained cant push PRs upstream? foo's repo -> my repo -> esthers repo, she shuold be able to PR to mine ey?

safe groveBOT
#

For a bot-user PAT, your list is a bit “too much by default”. I’d start tighter and add perms only when you hit a hard error.

Fine‑grained PAT perms (suggested baseline)

On Esther’s fork repos (repos she owns):

  • Contents: Read & write âś… (needed to push branches)
  • Pull requests: Read & write âś… (if you want her to open/modify PRs in her repos / interact with PR metadata)
  • Issues: Read & write âś… only if you want her to label/close/comment on issues (otherwise Read is fine)

I would not grant these unless you know you need them:

  • Actions: RW ❌ (very powerful; can edit workflows, trigger/abuse CI in some setups). If anything, keep Actions read-only.
  • Commit statuses: RW ❌ unless Esther is actually posting commit statuses (most bots don’t need to). CI usually sets statuses itself.

Big caveat you called out: “fine-grained can’t PR upstream”

Correct for automation. A fine‑grained PAT set to “all repos” means all repos owned by Esther — it does not cover your upstream repo.

Creating a PR targets the upstream repo (PR object lives there), so gh pr create --repo you/your-repo ... typically fails with a fine‑grained token unless that token is allowed to access that upstream repo (which it can’t, if it’s not owned by Esther).

#

What works if you want Esther to open PRs to your repo automatically

  • Use a classic PAT for Esther (public repos → usually public_repo scope is enough), or
  • Install a GitHub App on the upstream (back to App complexity), or
  • Accept “Esther pushes branch to her fork; you (or a different credential) opens the PR.”

Fast way to verify: try running as Esther:

gh pr create --repo YOU/UPSTREAM --head esther:branch --base main

If it 403s, that’s the fine‑grained limitation in action → switch Esther to a classic PAT (scoped minimally).

nimble smelt
#

rq before i generate a token. imma delete all the local githup app secrets on her server. How do i get the secret working? just place the PAT in .keys and then run...?

I know I need to run gh auth of some sort and then gh git-setup?

safe groveBOT
#

OpenClaw doesn’t have any built-in “put a token in .keys and it’s auto-used” behavior (unless you built that yourself). For gh/git, the clean way is: make the PAT available as an env var inside the container.

Minimal setup (recommended)

  1. Put the PAT into your podman-compose as a secret/env var, e.g.:
  • GH_TOKEN=... (this is what gh reads)
  • optionally also GITHUB_TOKEN=... (some tools look for this)
  1. Restart the container so the Gateway/agent exec sees it.

  2. Verify inside the same environment where commands run:

gh auth status
gh api user

Do you need gh auth login?

  • If you only use gh ... commands (issue list, pr create, comments): no — GH_TOKEN is enough.
#
  • If you also need git push over HTTPS and want it painless: do a one-time login + setup so git knows how to authenticate:
printf '%s\n' "$GH_TOKEN" | gh auth login --hostname github.com --with-token
gh auth setup-git

That writes to ~/.config/gh/hosts.yml and your git credential config, so in containers you’ll want that directory to be on a persistent volume if you don’t want to redo it on every rebuild.

TL;DR

  • Put PAT in GH_TOKEN in the container environment.
  • gh auth login --with-token + gh auth setup-git only if you need git push convenience/persistence.
nimble smelt
#
gh pr create --repo YOU/UPSTREAM --head esther:branch --base main

give me a specific example

ygypt/foorepo is my repo
bot/foorepo is hers

she wants to make a pr for a branch called feature/test

safe groveBOT
#

Assuming:

  • upstream repo: ygypt/foorepo
  • Esther’s fork: bot/foorepo
  • branch on Esther’s fork: feature/test
  • target base branch on upstream: main

Run (as Esther / with Esther’s token):

gh pr create \
  --repo ygypt/foorepo \
  --head bot:feature/test \
  --base main \
  --title "feature/test" \
  --body "PR from bot fork branch bot:feature/test"
#

Notes:

  • --repo is the upstream PR target.
  • --head bot:feature/test means “take the head branch from the bot fork”.
  • You can also add --draft if you want all bot PRs to start as drafts.

If you’re already in a checkout where origin points to bot/foorepo and upstream points to ygypt/foorepo, you can often omit --head and just do gh pr create --repo ygypt/foorepo --base main, but the explicit form above is the least ambiguous for automation.

nimble smelt
#

oh can you also add a line specifying where to make this push from? the repo wuold be in projects/foorepo, i make her use git -C projects/<project> to act on a specific folder but idk about gh

safe groveBOT
#

gh doesn’t have a -C <path> like git does. The usual pattern is just: run it from that directory (or wrap it in a subshell).

For your case:

( cd projects/foorepo && \
  gh pr create \
    --repo ygypt/foorepo \
    --head bot:feature/test \
    --base main \
    --title "feature/test" \
    --body "PR from bot fork branch bot:feature/test"
)

If you prefer the short repo flag, gh also supports -R:

#
( cd projects/foorepo && \
  gh -R ygypt/foorepo pr create \
    --head bot:feature/test \
    --base main \
    --title "feature/test" \
    --body "PR from bot fork branch bot:feature/test"
)

(You can sometimes create the PR from anywhere if you provide --repo/--head/--base, but in automation I’d still cd into the checkout so gh never complains about “not a git repository” / missing context.)

nimble smelt
#

bit messy. should i write a dedicated openclaw extension that wraps this for them so that they just need to write the project name, title, body? or is it best to keep these things raw so the bot knows exactly what theyre doing?

safe groveBOT
#

If it’s just “make this less annoying to type”, don’t jump straight to a full OpenClaw extension/plugin.

Best progression I’ve seen work:

1) Start with a tiny wrapper script (recommended)

Pros: deterministic, easy to audit, fewer LLM footguns, no OpenClaw internals.

Put something like bin/pr-create in the workspace and have the agent call that via exec, e.g.:

bin/pr-create foorepo feature/test "My title" "My body"

Inside the script you do the messy bits (cd projects/$project, gh -R ygypt/foorepo pr create --head bot:feature/test ...).

That keeps the “raw” gh command in one place, but keeps the agent interface clean.

#

2) If you need multi-step + approvals: use Lobster (not a plugin)

If the flow is “sync → branch/rebase → commit → push → PR” and you want it resumable + with approval gates, Lobster is a better fit than writing a gateway extension.
(See the “Lobster” section here: https://docs.openclaw.ai/automation/cron-vs-heartbeat)

3) Only write an OpenClaw extension if you want a new tool with a schema

That’s worth it when you want something like github_pr_create({project,title,body}) as a first-class tool with strict validation/policy. Otherwise it’s extra moving parts (build/versioning/debugging).

Net: wrapper script first, then Lobster if it grows into a real pipeline.

nimble smelt
#

I've never heard of lobster is this new? its basically a quick way to wrap complex execs into a mini-tool?

safe groveBOT
#

Yep — Lobster is relatively new in the OpenClaw world, and your summary is basically right:

  • It’s a typed workflow runtime that lets OpenClaw run a multi-step, deterministic pipeline as one tool call (instead of the LLM “freestyling” 15 separate exec calls).
  • It has built-in approval gates + resume tokens, so you can do “prep everything → show me a preview → only execute if approved” without re-running earlier steps.
  • Under the hood OpenClaw just runs a local lobster CLI in “tool mode” and parses a JSON envelope.

Where it fits for you:

  • Your flow (“sync → branch/rebase → commit → push → PR”) is exactly the kind of thing Lobster is meant for if you want reliable, repeatable automation.
  • If you just want to shorten cd projects/foorepo && gh ..., a tiny wrapper script is still simpler.

Docs: https://docs.openclaw.ai/tools/lobster

nimble smelt
#
~# ( cd projects/femtobot && \
  gh pr create \
    --repo ygypt/femtobot \
    --head feature/test \
    --base main \
    --title "feature/test" \
    --body "PR from bot fork branch bot:feature/test"
)

Creating pull request for feature/test into main in ygypt/femtobot

pull request create failed: GraphQL: Resource not accessible by personal access token (createPullRequest)

ye it failed

safe groveBOT
#

That error is exactly the fine‑grained PAT limitation biting you:

Resource not accessible by personal access token (createPullRequest)

With a fine‑grained PAT, GitHub generally won’t let the token “contribute” (create PR objects) against public repos the token owner doesn’t own / isn’t a member of. Your upstream ygypt/femtobot falls into that bucket, so PR creation fails.

Fix (for public upstream repos): use a classic PAT for Esther

Create a classic token on the bot account and give it the minimal scope:

  • public_repo (usually enough for public repos: create PRs, issues, etc.)

Then export it as GH_TOKEN and rerun.

Also: your --head should include the fork owner

Use:

#
( cd projects/femtobot && \
  gh pr create \
    --repo ygypt/femtobot \
    --head bot:feature/test \
    --base main \
    --title "feature/test" \
    --body "PR from bot fork branch bot:feature/test"
)

If you must stay fine‑grained

Then you’re back to “auth that has upstream access”, i.e.:

  • install a GitHub App on upstream, or
  • make Esther a collaborator on upstream (which you said you don’t want), or
  • have Esther only push to her fork and you open PRs manually (or via a different credential).
#

If you tell me whether ygypt/femtobot is under a GitHub org (orgs can restrict classic PATs), I can tell you if public_repo classic will work cleanly or if org policy will block it.

nimble smelt
#

well now that esther is simply a normal github account, adding her as a collaborator.. would that allow me to JUST give her account the rights to open PRs on my repo? or does that entail greater write access?

safe groveBOT
#

Adding Esther as a collaborator won’t give you a clean “PR-only” permission in the way you’re hoping.

Key points

  1. For public repos, Esther does not need to be a collaborator to open PRs
    A normal GitHub user can fork ygypt/foorepo → push branches to bot/foorepo → open a PR upstream.

  2. Your failure (createPullRequest “not accessible…”) is a fine‑grained PAT limitation, not a “not a collaborator” problem.
    Even if you add Esther as an outside collaborator, fine‑grained PATs still have gaps contributing to repos not owned by the token owner.

  3. If you want Esther to open PRs upstream via gh pr create, the simple fix is a classic PAT
    Use a classic PAT on the bot account (usually scope public_repo for public repos), and keep Esther not a collaborator on upstream. That’s actually the smallest blast radius.

If you do add Esther as a collaborator anyway

You can reduce risk by giving the lowest role that still lets her do PR/issue housekeeping:

  • Read: safest, but may be limiting for PR/issue management features
#
  • Triage (if available): can manage issues/PRs but can’t push code
  • Avoid Write/Maintain/Admin if your goal is “never touch upstream branches”

But: GitHub permissions are still fairly “bucketed” — you can’t grant only “open PRs” and nothing else.

Recommendation

  • Keep upstream locked down.
  • Don’t add Esther as collaborator.
  • Switch Esther to a classic PAT for automation, and create PRs from fork:
( cd projects/foorepo && gh pr create -R ygypt/foorepo --head bot:feature/test --base main )

If you tell me whether you see the Triage role option on your repo/org, I can tell you exactly what that would allow beyond PR creation.

nimble smelt
#

so classic token will enable my bot to have nearly the same perms on their repos as a proper gh auth login authenticated account?

safe groveBOT
#

Mostly yes.

A classic PAT is basically “a password for the GitHub API + HTTPS git” for that bot user, limited by:

  1. the token’s scopes (e.g. public_repo vs repo), and
  2. whatever permissions the bot user account has (org/repo access, SSO policies, etc.).

So if Esther is a normal GitHub account, a classic PAT with the right scope will let gh do essentially the same things as a “real” gh auth login session (create PRs, comment, list issues, etc.)—and it should fix the createPullRequest “Resource not accessible…” you hit with fine‑grained.

Practical tip: you can even make it literally a gh-login session by doing:

printf '%s\n' "$GH_TOKEN" | gh auth login --hostname github.com --with-token

For public repos, start with classic scope public_repo. Only go bigger if you hit a permission error.

nimble smelt
#

i could also just straight up run gh auth login and get the code from firefox on my pc and this would all be kinda moot yeah? how long does gh auth login last?

safe groveBOT
#

Yeah, you can just run gh auth login, paste the device code in Firefox on your PC, and be done.

Two different “lifetimes” to keep straight:

  1. The one-time device code (the XXXX-XXXX you paste into github.com/login/device)

    • short-lived (minutes). If you wait too long you have to restart the login flow.
  2. The actual token that gh auth login stores

    • effectively doesn’t expire on its own; it lasts until you revoke it (or org/SSO policy invalidates it, password reset/security event, etc.).
    • You can check it anytime with:
      gh auth status
      

If you do this on a server/container, two practical gotchas:

  • Persistence: gh auth login stores creds under ~/.config/gh (or a credential store). If your container doesn’t persist $HOME, you’ll “lose login” on rebuild/recreate. Mount a volume for the home dir (or at least ~/.config/gh).
#
  • Git pushing: gh being logged in doesn’t automatically make git push stop prompting unless you set up the credential helper. Run once:
    gh auth setup-git
    
    (This wires git to use gh auth git-credential.)

For pure automation, GH_TOKEN=... is still the cleanest because it’s explicit and stateless — but gh auth login is totally fine if you persist the config.

nimble smelt
#

might do that, but for classic token anyways, would i just give the blanket "repo" perm?

safe groveBOT
#

No — don’t start with blanket repo unless you actually need private repo access.

Given you said “assume everything is public”, a classic PAT with:

  • public_repo âś…

is usually enough for: push to Esther-owned public repos, create PRs upstream on public repos, comment, issues, etc.

Only add scopes when you hit a real 403:

  • read:org (only if you’re interacting with org-owned repos / teams and gh complains)
  • workflow (only if Esther needs to dispatch/modify GitHub Actions; otherwise skip—this is powerful)

Why not repo? It’s the “god scope” for repo access (incl. private) and classic PATs aren’t repo-selectable, so it’s a much bigger blast radius than you need.

nimble smelt
#

surely it should be able to manage its own private repos yeah?

safe groveBOT
#

If you mean private repos owned by Esther’s account: yes, she can manage them — but the token has to be allowed to touch private repos.

  • Classic PAT
    • public_repo → public only
    • repo → public + private (this is the “private repos” switch)

So if you want a single classic token that can work on Esther’s private repos too, you’re basically signing up for repo.

Cleaner/safer pattern (what I’d do):

  • Use a fine‑grained PAT for “all repos owned by Esther” (covers her private repos nicely, least blast radius).
  • Use a separate classic public_repo token only for the one thing fine‑grained is bad at in your workflow: creating PRs upstream on public repos.

That way you avoid giving a never-expiring classic repo token more power than necessary.

nimble smelt
#

if it has both tokens, when it tries to use gh, gh would just choose the most relevant token?

safe groveBOT
#

No — gh won’t “pick the most relevant token” based on which repo you’re targeting.

gh uses one auth context at a time, in roughly this priority order:

  1. GH_TOKEN env var (if set)
  2. whatever is stored from gh auth login (active account)

So if you want to keep both a fine‑grained token and a classic token, you have to choose explicitly per command, e.g.:

# Use classic token for “PR into someone else’s repo”
GH_TOKEN="$ESTHER_CLASSIC_TOKEN" gh pr create -R ygypt/foorepo --head bot:feature/test --base main

# Use fine-grained token for operations on Esther-owned repos
GH_TOKEN="$ESTHER_FINE_GRAINED_TOKEN" gh issue list -R bot/foorepo
#

Practical tip: bake that into your wrapper script so the agent never has to think about it.

Also: gh auth switch only helps when you have multiple accounts logged in; it doesn’t auto-switch between “two tokens for the same user” in a smart way.

nimble smelt
#

okay, for now I'm going to just use the classic with public_repo but if i need to give her private repos I'll keep that in mind

safe groveBOT
#

That’s a good call.

Classic PAT + public_repo is the sweet spot for your setup right now:

  • enough for gh pr create upstream (public)
  • small-ish blast radius compared to repo

When/if you want her managing private repos too, the two sane options are:

  • switch classic scope to repo (big hammer), or
  • keep classic public_repo for upstream PRs and add a fine‑grained “all Esther-owned repos” token for her private stuff (then pick via GH_TOKEN=... per command / wrapper).

If this thread solved it, please mark the solution message so Answer Overflow indexes it (right‑click → Apps → ✅ Mark Solution).

nimble smelt
#

lets move forward and create a lobster for esther

safe groveBOT
#

Do this as a first “v0” Lobster workflow: push current HEAD to the fork branch + open PR upstream, with an approval gate in between.

0) Preconditions

  • lobster CLI is installed in the same environment as the Gateway (container/host) and on PATH
  • OpenClaw has the tool enabled (config):
    { "tools": { "alsoAllow": ["lobster"] } }
    
  • GH_TOKEN is set to Esther’s classic PAT (so gh pr create works)
  • In projects/<project> your remotes are:
    • origin → Esther fork (bot/foorepo)
    • PR target is upstreamRepo (e.g. ygypt/foorepo)

1) Create this workflow file in Esther’s workspace

Save as (example):
~/.openclaw/workspace/workflows/esther-pr-create.lobster.yaml

#
name: esther-pr-create
args:
  project: { description: "folder under projects/, e.g. femtobot" }
  upstreamRepo: { description: "PR target, owner/repo, e.g. ygypt/femtobot" }
  forkOwner: { description: "fork owner login, e.g. bot" }
  headBranch: { description: "branch name, e.g. feature/test" }
  baseBranch: { default: "main" }
  title: { default: "" }
  body: { default: "" }
  draft: { default: true }

steps:
  - id: preview
    command: |
      cd "projects/${project}"
#
      echo "Upstream: ${upstreamRepo}"
      echo "Head: ${forkOwner}:${headBranch}"
      echo "Base: ${baseBranch}"
      echo ""

      echo "Git status:"
      git status --porcelain=v1 || true
      echo ""

      echo "Recent commits:"
      git --no-pager log --oneline -n 10 || true
      echo ""

      echo "Diff stat vs origin/${baseBranch}:"
      git --no-pager diff --stat "origin/${baseBranch}...HEAD" || true
#

  - id: gate
    command: echo '{"prompt":"Push branch + open PR now?","items":[]}'
    approval: required

  - id: push
    command: |
      cd "projects/${project}"
      CUR="$(git rev-parse --abbrev-ref HEAD)"
      if [ "$CUR" != "${headBranch}" ]; then
        echo "Not on expected branch. On: $CUR, expected: ${headBranch}"
        exit 2
      fi
      git push -u origin "HEAD:${headBranch}"
    condition: $gate.approved
#

  - id: pr
    command: |
      cd "projects/${project}"

      TITLE="${title}"
      if [ -z "$TITLE" ]; then TITLE="${headBranch}"; fi

      BODY="${body}"

      DRAFT=""
      if [ "${draft}" = "true" ]; then DRAFT="--draft"; fi

      gh pr create \
        -R "${upstreamRepo}" \
#
        --head "${forkOwner}:${headBranch}" \
        --base "${baseBranch}" \
        --title "$TITLE" \
        --body "$BODY" \
        $DRAFT
    condition: $gate.approved

2) Run it (from chat, tell Esther agent)

“Run the Lobster workflow workflows/esther-pr-create.lobster.yaml with args: …”

Args example for your earlier case:

{
#
  "project": "femtobot",
  "upstreamRepo": "ygypt/femtobot",
  "forkOwner": "bot",
  "headBranch": "feature/test",
  "baseBranch": "main",
  "title": "feature/test",
  "body": "Automated PR from Esther",
  "draft": true
}

You should see a needs approval pause after preview, then after approval it will push + pr.

If you want, next iteration we can add the “sync main → rebase working branch” steps safely (they’re more destructive, so we’ll add another approval gate before any rebase).

nimble smelt
#

do approvals integrate with discord

safe groveBOT
#

Yes — but it’s not a Discord-native “button UI” by default.

How it works:

  • Lobster hits an approval: required step → the tool returns status: needs_approval plus a resumeToken.
  • OpenClaw/the agent will surface that prompt in the chat (Discord in your case).
  • You approve by replying in Discord (e.g. “approve”, “yes, do it”), and the agent then calls Lobster again with action:"resume", token:<resumeToken>, approve:true.

Two gotchas:

  • No inline buttons (unless you build/enable that separately); it’s text approval.
  • If you run Lobster inside a cron job, it can pause at approval, but it won’t magically resume—someone (you) still has to approve in chat so the agent can resume with the token.
nimble smelt
#

can you create a lobster that doesnt have an approval step

safe groveBOT
#

Yes — if by “approval step” you mean Exec approvals (the “Allow once / Always allow / Deny” prompt when the agent tries to run host commands like git, gh, etc.).

You can make an agent run fully unattended by configuring exec approvals so it never prompts:

Option A (recommended): no prompts, but still safe-ish (allowlist)

Set the agent’s exec approvals to security: "allowlist" + ask: "off", and allowlist only the binaries you want (e.g. git, gh).

You can do this in Control UI → Nodes → Exec approvals (pick Gateway or a Node, then pick the agent scope).

Docs: https://docs.openclaw.ai/tools/exec-approvals

Option B (dangerous): no prompts, full access

Set security: "full" + ask: "off" for that agent. That’s “no approval step”, but it also means the agent can run basically any command on that host.

Important gotcha for “headless” runs (cron/heartbeat)

If a prompt would be required but there’s no UI reachable, the default fallback is effectively “deny”, so fully autonomous workflows need ask: off (or a complete allowlist) anyway.

#

Related docs:

If you tell me whether your agent is executing on sandbox vs gateway vs node (and whether your agent id is just main), I can point you at the cleanest setup for “auto-make PRs every heartbeat” without interactive approvals.

nimble smelt
#

i already have that setup im asking if you could do me the honor of removing the approval step from that lobster XD

safe groveBOT
#

Can’t do it for you from here (I don’t have access to edit your config / approvals file), but you can absolutely “de-lobster” the approval prompts yourself.

If you want zero approval UX ever for an agent, set that agent’s exec approvals to:

  • ask: "off" (this is the “no prompts” switch)
  • and either:
    • security: "allowlist" (still restricted to allowlisted binaries), or
    • security: "full" (no restrictions; most dangerous)

You can do it in Control UI → Nodes → Exec approvals (pick Gateway vs Node, then pick the agent scope), or directly in ~/.openclaw/exec-approvals.json on the execution host.

Docs: https://docs.openclaw.ai/tools/exec-approvals

If you already set allowlists but still see prompts, 99% of the time it’s because ask is still on-miss or the allowlist pattern isn’t matching the resolved binary path (use the “last resolved path / last used” info in the UI to add the correct pattern).

nimble smelt
#

krill you just sent me a lobster a few hours ago... im asking you to work with me to improve it lol

safe groveBOT
#

I’m down to improve it — but I can’t see the “lobster I sent you earlier” in this thread history. Paste the lobster prompt/config you’re using (redact tokens), and tell me which “no approval” flavor you want:

1) No prompts, still constrained (recommended)

This is “the lobster never asks you anything” but it also won’t run unknown commands.

You need both layers set to no-prompt, because the effective policy is the stricter one:

  • ~/.openclaw/exec-approvals.json (on the exec host):
    defaults.ask: "off" and defaults.security: "allowlist"
  • OpenClaw exec defaults (config): make sure you’re not still at tools.exec.ask: "on-miss" somewhere (that alone will keep producing approval prompts)

Then you just pre-allowlist the exact resolved paths for git, gh, etc.

2) No prompts, no constraints (dangerous)

This is the real “remove the approval step entirely”.

  • easiest operationally: send once in the chat where the lobster runs:
#

/elevated full
(this skips exec approvals by design: https://docs.openclaw.ai/tools/elevated)

  • or set the global default: agents.defaults.elevatedDefault: "full" (same effect, but automatic)

If you’re still seeing prompts “even though ask is off”

Almost always one of these:

  • you edited approvals for Gateway but exec is happening on a Node (or vice versa)
  • your allowlist pattern doesn’t match the resolved binary path (e.g. /usr/bin/git vs /usr/local/bin/git)

Paste (redacted) outputs and I’ll tell you exactly what’s mismatched:

  • openclaw config get tools.exec
  • openclaw approvals get --gateway (and --node <your-node> if you use one)

Now paste the lobster you mean, and I’ll rewrite it to match whichever mode you pick.

nimble smelt
# safe grove ```yaml name: esther-pr-create args: project: { description: "folder under pro...

i want it unconstrained, my bot is running a very containerized environment (not using the openclaw sandbox, like actual containers) and its supposed to be a bit of a playground, i want her to break things.

before we tackle the lobster I actually want to ask you about this error:

~# ( cd projects/femtobot &&   gh pr create     --repo ygypt/femtobot     --head esther:feature/dingus     --base main     --title "Add dingus"     --body "Add dingus upstream as a test"; )

Creating pull request for esther:feature/dingus into main in ygypt/femtobot

pull request create failed: GraphQL: Head sha can't be blank, Base sha can't be blank, Head repository can't be blank, No commits between ygypt:main and esther:feature/dingus, Head ref must be a branch, not all refs are readable (createPullRequest)
safe groveBOT
#

That gh pr create error is basically GitHub saying: “I can’t resolve/read the head ref you gave me, and even if I could, it looks like there may be no diff anyway.”

A few of those messages are cascade failures:

  • “Head sha/base sha can’t be blank / head repository can’t be blank” → GH couldn’t look up the refs, so it ends up with empty SHAs/repo.
  • “Head ref must be a branch” → the ref it resolved (if any) wasn’t a normal refs/heads/... branch.
  • “not all refs are readable” → auth/permissions or the fork/branch simply doesn’t exist (or is private/inaccessible to the token).
  • “No commits between ygypt:main and esther:feature/dingus” → even if the ref exists, it’s identical to base (no new commits).

Most common root causes + fixes

1) The branch was never pushed to the fork (or fork doesn’t exist)

From inside projects/femtobot, run:

git remote -v
#
git branch --show-current
git log --oneline --decorate -5

You want:

  • a branch like feature/dingus checked out locally
  • at least 1 commit on it that’s not on upstream/main

Then push it to the fork remote (usually origin):

git push -u origin feature/dingus

After that, this should succeed:

#
gh pr create --repo ygypt/femtobot --base main --head esther:feature/dingus --title "Add dingus" --body "Add dingus upstream as a test"

2) Your origin/upstream remotes are flipped (common in automation)

You typically want:

  • origin → esther/femtobot (fork)
  • upstream → ygpt/femtobot (target)

Check with git remote -v. If you cloned the upstream repo directly and never added the fork remote, gh will happily let you create a local branch that doesn’t exist on GitHub anywhere.

3) Auth can’t read the fork/branch (“not all refs are readable”)

Confirm what identity gh is using and that it can see the fork:

gh auth status
#
gh repo view esther/femtobot --json nameWithOwner,isPrivate

If gh repo view esther/femtobot fails, then --head esther:feature/dingus will never work with that token (wrong account logged in, missing repo scope for private repos, fork doesn’t exist, etc.).

4) There are actually no commits on the branch

If this prints nothing meaningful:

git log --oneline upstream/main..HEAD

…then GH is right: there’s no diff, so PR creation fails. Make a commit (or rebase onto the right base), push again, then retry.