Polyhydra Skills  /  GitHub & Delivery

straighten-the-road

Prep a portfolio of repos across ~/projects for a multi-day unattended coding-agent burn (Codex or otherwise) — narrow scope to the repos actually in play, clear local/PR/branch turbulence, and make sure every in-scope repo has queued work. Use when the user says "straighten the road", "get ready for the burn", "clear the runway", wants repos "in good shape" before a multi-day agent session, or asks what's blocking a clean unattended run across their portfolio.

GitHub & Delivery

Drop this in — save the block below as ~/.claude/skills/straighten-the-road/SKILL.md, or run:

mkdir -p ~/.claude/skills/straighten-the-road
cat > ~/.claude/skills/straighten-the-road/SKILL.md <<'EOF'
# (paste the full source block below into this file)
EOF

Full source

SKILL.md — copy everything inside
# Straighten the Road

Prep work before a long unattended coding-agent run (a multi-day Codex credit
burn, a haiku-storm sweep, or any stretch where agents work without a human
watching closely). The goal: no repo should make an agent stop and wait on a
human decision it didn't need to hit — dirty trees, missing branches, stale
competing PRs, or nothing queued to work on.

This is a **playbook**, not a single script — most steps involve judgment
calls that should go through `AskUserQuestion` (this user runs MAID; see
their global CLAUDE.md) rather than being silently decided. Scale the depth
to what the user asks for; the steps below are the full sequence this skill
was extracted from, not a mandatory checklist.

## 0. Scope the portfolio down before touching anything

`~/projects` typically holds 200+ repos across 40+ hubs — most quiet,
legacy, or backend/infra that isn't in play for a given burn. Don't audit
all of them by default.

1. Run the audit (git-dirty / PR-workspace signal per hub, requires
   `~/code/dev-forge`):
   ```sh
   cd ~/code/dev-forge && python3 tools/project_focus.py \
     --projects-root ~/projects audit --no-github \
     --output-dir "$CLAUDE_JOB_DIR/tmp/project-focus" >/dev/null
   ```
   Read `project-focus.json`'s `active[]` entries: `name`, `repositories[]`
   (`github_slug`, `path`, `dirty`), `dirty_repository_count`,
   `pr_workspace_count`.
2. Ask the user to narrow scope — hub-level exclusion (internal
   tooling/infra vs. product hubs) is usually the first cut, then app-shape
   (mobile/web/desktop surface, not backend/API/library) and recency (local
   commit within the last few weeks is a reasonable "actively worked on"
   bar) narrow further. Compute recency locally and cheaply, no API calls:
   ```sh
   git -C "$path" log -1 --format=%ct   # then diff against `date +%s`
   ```
   This is a real fork in the road — use `AskUserQuestion`, don't guess the
   full list silently. Expect 2-3 rounds as the user corrects the cut
   ("pull out the non-user-facing apps", "mobile/web/desktop apps" is a
   realistic sequence of refinements).

## 1. Sweep GitHub state for the scoped list

For each repo in the final scope, gather: default branch, whether `dev`
exists, open PRs (draft/mergeable/updated_at/title), open issue count. Do
this with a small parallel `gh api` sweep rather than one call per fact —
see codeburn's pattern for spawning workers, or just `xargs -P 10` over a
slug list for a read-only sweep this size.

**Gotcha — branch-existence false positive:** `GET
/repos/{owner}/{repo}/git/refs/heads/{branch}` does **prefix matching**, not
exact matching. Checking for `dev` this way silently succeeds against any
branch starting with "dev" (`develop`, `dev-forge-fix`, etc.), so a repo
that actually lacks a `dev` branch reads as having one. Use `GET
/repos/{owner}/{repo}/branches/{branch}` instead — that 404s on anything
that isn't an exact match. Verify with 3-4 known-missing repos by hand
before trusting a bulk result; this bit a real run and produced a
completely wrong "44/45 already have dev" false-negative on the first pass.

## 2. Save the inventory somewhere durable

The scoped list + snapshot state is worth keeping — write it to
`~/projects/<SOMETHING>-<date>.md` (the `~/projects` root is itself a git
repo, `Polyhydra-Games/projects-hub`, that versions exactly this kind of
hub-level planning doc) and commit it via a worktree + draft PR (see
`~/.claude/skills/projects/SKILL.md`'s **launch** section and this user's
CLAUDE.md worktree/PR-shipping rules). Also save a `project`-type memory
entry pointing at the file — the PR/issue counts are a snapshot, say so
explicitly so a future read doesn't trust stale numbers.

**Gotcha — worktree base branch:** `EnterWorktree`'s default `baseRef`
setting branches from `origin/<default-branch>` (usually `main`), **not**
whatever the repo's actual integration branch is. If the repo uses `dev` as
its working branch (per this user's "main triggers real deploys, work on
dev" rule), a worktree created the normal way silently bundles in every
commit that's on `main` but not yet on `dev` into your diff. Check
`git branch -a` for a `dev` branch right after entering the worktree; if one
exists and diverges from `main`, `git fetch origin dev && git reset --hard
origin/dev` before making any edits, then re-apply. Verify with `git diff
--stat origin/dev..HEAD` before pushing — it should show only your file(s).

**Gotcha — backticks in shell heredocs:** if you build markdown content
with inline code spans (`` `like this` ``) inside a `python3 -c "..."`
double-quoted Bash string, the backticks get interpreted as shell command
substitution *before Python ever sees the string* — content silently
vanishes or executes as garbage commands. Either write the file via the
`Write`/`Edit` tools directly (no shell involved), or use a single-quoted
heredoc (`<<'EOF'`) which suppresses all shell expansion.

**Gotcha — GitKraken PR creation on org repos:** `pull_request_create`
fails on repos under an org (e.g. `Polyhydra-Games/*`) with a generic MCP
error. Fall back to `gh api repos/{owner}/{repo}/pulls -f title=... -f
body=... -f head=... -f base=... -F draft=true` — **not** `gh pr create`,
which this user's `gitkraken-hooks` plugin hard-denies over Bash regardless
of org. The REST endpoint doesn't match that string and isn't blocked.

## 3. Mechanical fixes (low-risk, confirm the class of action once)

Once the user says "do it" for a category, execute the whole category — but
each individual mutating call as a **separate direct tool call**, not a
`for` loop in one Bash invocation. A batch loop that merges/creates
many things in a single script is more likely to get blocked by the auto
mode classifier than the same actions issued one at a time; if blocked,
retry as individual calls before escalating to the user.

- **Missing `dev` branches**: create off the tip of each repo's existing
  default branch via `gh api repos/{slug}/git/refs -f ref=refs/heads/dev -f
  sha=<default-branch-tip-sha>`. Skip repos that already have an equivalent
  differently-named integration branch (e.g. `develop`) rather than
  creating a confusing duplicate.
- **Safe automated/bot PRs** (dependency bumps, scheduled sync workflows,
  etc.): check `mergeable_state` and actual CI check-runs
  (`gh api repos/{slug}/commits/{sha}/check-runs`) before merging — draft
  PRs need `markPullRequestReadyForReview` (GraphQL) before the REST merge
  endpoint will accept them.

## 4. Seed work for repos with nothing queued

A repo with zero open issues has nothing for an unattended agent to pick up.
Don't fabricate issues from nothing — fan out research agents (one per
handful of repos, `Explore` subagent type is enough since this is read-only)
to actually look at each repo (README/AGENTS.md, `gh issue list --state
closed`, `gh pr list --state merged`, a skim for TODO/FIXME or thin test
coverage) and either propose one concrete bounded issue or recommend
"skip" with a real reason (issues disabled, explicitly declared
abandoned/unmaintained, a genuine placeholder with no source tree yet).
Review the proposals before creating anything — a subagent's proposal is a
submission, not a result. Create via `gh issue create --repo <slug> --title
"..." --body "$(cat <<'EOF' ... EOF)"` (heredoc body, same backtick trap as
above applies to inline code spans in the body).

## 5. Review stale substantive PRs — don't auto-merge these

Draft PRs carrying real logic (security fixes, bug fixes, feature work) need
a human decision, not a mechanical merge, even when CI is green. For each:
check the PR body for an explicit sequencing note (e.g. "requires backend
endpoint in PR #N, not deployed yet — merge that first"), check whether a
failing CI check is actually caused by this PR's diff or is pre-existing
breakage on the base branch (`gh api repos/{slug}/commits/<base-branch-tip>/check-runs`
— if the base branch itself is already red, the PR isn't the cause), and
check `mergeable_state` for `CONFLICTING` across a family of PRs that touch
overlapping files (a sign they're a stacked/sequenced chain that needs
human-decided merge order, not independent PRs). Report findings, don't act
unilaterally.

## 6. Close the loop

Update the inventory file and memory entry to reflect what actually
happened (branches created, PRs merged, issues seeded, what's still open) —
don't leave a stale "gap" list sitting in a doc once the gaps are closed.
Push the update to the same PR branch rather than opening a duplicate PR.

End with `AskUserQuestion` offering real next steps (review the flagged
stale PRs, clean any remaining dirty repos, or "now go!" if the list is
clean) — never end mid-audit without a clear handback.