LEGACY (v2). As of triage-clarify-v2, the cloud-side workflow handles the full Issue → Triage → Clarify → Develop loop end-to-end. This local stack is retained only as the v1 reply-path fallback (issues where cloud decided
decision==replyand the maintainer wants local re-analysis). For new setups, defaultPOLL_ENABLED=falseand use the cloud pipeline documented indocs/quickstart-clarify.md.Specifically, the cloud v2 pipeline replaces:
handle-triage.sh→ cloudclarify-loop.yml(Module 3' multi-turn clarification)handle.sh→ clouddevelop.yml(Module 4 with CLAUDE_DEV_PAT-as-developer)Coexistence contract:
poll.shchecksPOLL_ENABLEDenv var (defaultfalse). SetPOLL_ENABLED=truein~/.config/githubautodev/config.shonly if you need the v1 local fallback alongside the v2 cloud pipeline. The v1 path is mutually exclusive with v2'sneeds-clarifylabel — seedocs/labels.md#coexistence-v1--v2.
The hybrid architecture: cloud-side workflow analyses + comments + labels, local claude code implements. See docs/quickstart-triage.md for the cloud half.
This directory contains the local half:
scripts/local/
├── poll.sh # cron entry; polls for `accepted` AND `needs-ralph` issues, dispatches per label
├── handle.sh # develop-path handler (accepted label): claude + push + PR
├── handle-triage.sh # triage-path handler (needs-ralph label): claude + ralph → JSON + comment
└── README.md # this file
┌─ GitHub (cloud) ───────────────────────────────────────────────┐
│ Issue opened │
│ ↓ │
│ triage-issue.yml workflow runs │
│ ↓ │
│ Claude analyses (via GLM passthrough) │
│ ↓ │
│ decision==work OR confidence<TRIAGE_RALPH_THRESHOLD ? │
│ YES → applies `needs-ralph` + posts "queued" comment │
│ NO → posts reply comment (+ optional `accepted` if high) │
└────────────────────────────────────────────────────────────────┘
│
│ (label is the handoff signal)
▼
┌─ Your mac (local) ─────────────────────────────────────────────┐
│ cron → poll.sh (dual-label polling) │
│ ↓ │
│ gh issue list --label needs-ralph + --label accepted │
│ ↓ │
│ per-issue dispatch: │
│ • needs-ralph only → handle-triage.sh │
│ • accepted only → handle.sh │
│ • BOTH (race) → accepted wins (AC-P4) │
│ ↓ │
│ handle-triage.sh: claude -p + Skill(ralph) writes JSON │
│ ↓ │
│ wrapper posts formatted comment, applies `triage-done`, │
│ removes `needs-ralph`. Never applies `accepted` (S2). │
└────────────────────────────────────────────────────────────────┘
poll.sh polls both accepted and needs-ralph labels in two parallel gh issue list calls (GitHub's compound --label a,b is AND, not OR — so two calls are needed). Results are merged and deduped by issue number.
Race priority (AC-P4): if a maintainer applies accepted to an issue that's still labelled needs-ralph (during ralph's 5-15 min window), accepted wins. poll.sh dispatches to handle.sh only, removes needs-ralph, and marks the issue seen so handle-triage.sh doesn't subsequently fire on it.
Create ~/.config/githubautodev/config.sh:
# Required
export GITHUB_REPO="akushonkamen/github-auto-dev-scaffold"
export GITHUB_TOKEN="$(gh auth token)"
# Optional overrides
export CLAUDE_BIN="claude" # or /full/path/to/claude
export BASE_BRANCH="dev"
export DRAFT_PR="true" # open PRs as draft by default
export MAX_TURN_MINUTES="15"
export DRY_RUN="false" # true = run claude but skip push/PRchmod 600 ~/.config/githubautodev/config.sh
chmod +x scripts/local/poll.sh scripts/local/handle.shgh auth status
# Must show: ✓ Logged in to github.com account ...which claude
claude --version # whatever version prints; we just want it on PATHIf you're using a GLM proxy locally (e.g. ANTHROPIC_BASE_URL set in your shell), claude here will use that. The local stack does not override your claude config — it inherits whatever your shell sets.
Before installing cron, run poll.sh once to verify wiring:
./scripts/local/poll.shExpected output on an empty repo:
[...] [info] polling akushonkamen/github-auto-dev-scaffold for issues labelled 'accepted'
[...] [debug] no issues labelled 'accepted' found
Edit crontab:
crontab -eAdd (every 2 minutes — adjust to taste):
*/2 * * * * /Users/you/projects/GithubAutoDev/scripts/local/poll.sh >> /tmp/githubautodev-poll.log 2>&1
Caveat: cron has a minimal environment. If claude, gh, jq, or git aren't on cron's PATH, either:
- Prefix the cron entry with
PATH=...(cron syntax allows env assignments before the command), or - Source
~/.zshrc/~/.bashrcfrom insideconfig.sh, or - Use absolute paths in
CLAUDE_BIN, etc.
- Open a real issue on
akushonkamen/github-auto-dev-scaffold. - Wait for the cloud-side workflow to apply
accepted(setAUTO_ACCEPT_ENABLED=truerepo variable first, or applyacceptedmanually). - Within 2 minutes (cron cadence), poll.sh picks it up.
- Watch
/tmp/githubautodev-poll.logfor progress. - Final state: a new draft PR linked to the issue.
$XDG_STATE_HOME/githubautodev/ (default: ~/.local/state/githubautodev/)
├── seen.txt # issue numbers successfully handled (either path)
├── failed.txt # develop-path (handle.sh) failures
├── triage-failed.txt # triage-path (handle-triage.sh) failures
├── poll.lock.d/ # mkdir-based single-flight lock (auto-removed on exit)
└── handle-triage-N.lock.d/ # per-issue lock (prevents concurrent handle-triage.sh on same issue)
$PROJECT_ROOT/.omc/state/ (per-project analysis state — gitignored)
├── triage-prd-N.md # ralph PRD scaffold for issue #N (wrapper-authored)
├── triage-issue-N.json # ralph's structured analysis output (10-field schema)
└── labels-allow.txt # cache of repo labels minus deny-list (used by AC-H11)
To retry a failed issue:
sed -i.bak '/^42$$/d' ~/.local/state/githubautodev/failed.txtTo re-run a successfully handled one (e.g. you merged the PR but the issue reopened):
sed -i.bak '/^42$/d' ~/.local/state/githubautodev/seen.txt| Symptom | Cause | Fix |
|---|---|---|
claude binary not found |
CLAUDE_BIN not on cron's PATH |
Set CLAUDE_BIN=/absolute/path in config.sh |
uncommitted changes in $PROJECT_ROOT |
You have local edits | Commit or stash before next poll |
claude exited non-zero |
Local claude error (model, prompt, MCP) | Re-run claude -p manually to reproduce |
produced no commits |
Claude succeeded but didn't commit | Re-run; check if claude needs an explicit "commit" instruction |
push rejected (non-fast-forward) |
Branch diverged | Delete local feat/issue-N and re-run |
Could not resolve host inside cron |
Minimal cron env | Source ~/.zshrc from config.sh |
handle-triage.sh: schema validation failed |
Ralph output JSON doesn't match 10-field schema or AC-S2/S3 consistency | Inspect .omc/state/triage-issue-N.json; re-queue by removing/re-adding needs-ralph |
AC-H13 violation |
Wrapper attempted to apply a denied label (accepted/rejected/etc.) | This is a wrapper bug — open an issue; ralph output should never include state labels in suggested_labels |
Issue already accepted; ralph analysis skipped (AC-H14) |
Maintainer raced ralph and applied accepted first |
Expected behavior; no action needed |
GITHUB_TOKENlives in~/.config/githubautodev/config.sh— file mode 600.- The local claude CLI runs as your user with all your MCP servers and tools. This is more powerful than the cloud-side action (which is sandboxed). Only run this on machines you trust.
poll.shdoes NOT pull issue bodies into the local working tree — only into amktempfile that's removed on exit.- The PR body contains the issue URL but never the issue body verbatim (avoids re-rendering untrusted content).