{"slug": "a-zero-dependency-github-issue-poller-for-multi-agent-coding-teams", "title": "A zero-dependency GitHub Issue poller for multi-agent coding teams", "summary": "A developer has created a zero-dependency GitHub Issue poller designed for multi-agent coding teams. The Node.js script, named gh-agent-watch.js, monitors a repository for issues labeled with specific agent roles—such as \"agent:tester\" or \"agent:implementer\"—and signals the appropriate AI agent when new work arrives or existing tickets are updated. The tool enforces a routing system where agents claim, reassign, and resolve issues through label-based queues, eliminating the need for manual polling.", "body_md": "-\n-\nSave atraining/d666e2d20e5abfdf92eb74a0d5f4918d to your computer and use it in GitHub Desktop.\n\n[Learn more about bidirectional Unicode characters](https://github.co/hiddenchars)\n\n| #!/usr/bin/env node | |\n| /** | |\n| * gh-agent-watch.js — GitHub-Issue poller for multi-agent coding teams | |\n| * | |\n| * ============================================================================= | |\n| * WHAT THIS DOES (for AI agents reading this file) | |\n| * ============================================================================= | |\n| * | |\n| * This script is your \"inbox watcher\". It polls a GitHub repo for issues | |\n| * labelled with your agent name (e.g. \"agent:tester\") and wakes you whenever | |\n| * something new arrives or an existing issue is updated. | |\n| * | |\n| * You run it once at startup inside a persistent Monitor process. Every line it | |\n| * prints to stdout is a signal for your orchestrator to wake you and let you | |\n| * act. Between wakes, you sleep. You do NOT need to poll manually. | |\n| * | |\n| * ============================================================================= | |\n| * TEAM MODEL (generic, rename roles to fit your project) | |\n| * ============================================================================= | |\n| * | |\n| * Any number of agents can participate. Each has a unique role name. Common | |\n| * example set: | |\n| * | |\n| * implementer — writes and commits code (the only agent with write access | |\n| * to source files; all others review, advise, or document) | |\n| * tester — writes test cases, user journeys, and acceptance criteria; | |\n| * reports failures as new issues to implementer | |\n| * reviewer — code review, architecture, and technical quality gating | |\n| * docs — user-facing documentation; touches no source code | |\n| * legal — compliance, licensing, regulatory gating before release | |\n| * market — competitive analysis, domain model, product direction | |\n| * | |\n| * You can use any names you like. The only contract: every agent's name must | |\n| * match exactly the label suffix used in GitHub, e.g. agent:tester. | |\n| * | |\n| * ============================================================================= | |\n| * HOW ROUTING WORKS | |\n| * ============================================================================= | |\n| * | |\n| * Issues are routed by label. An issue labelled \"agent:tester\" sits in the | |\n| * tester's queue. Routing rules (for AI agents): | |\n| * | |\n| * - To send an issue TO another agent: | |\n| * gh issue edit <num> --add-label \"agent:<target>\" \\ | |\n| * --remove-label \"agent:<you>\" | |\n| * Add a one-line comment explaining WHY you are re-routing. | |\n| * | |\n| * - To create a new issue for another agent: | |\n| * gh issue create --repo <owner/repo> \\ | |\n| * --title \"<Area>: <what> (<why>)\" \\ | |\n| * --label \"agent:<target>\" --label \"<theme>\" \\ | |\n| * --body \"...\" | |\n| * | |\n| * - To respond to the current owner of an issue: comment on it. | |\n| * gh issue comment <num> --body \"...\" | |\n| * | |\n| * - To close a resolved issue: | |\n| * gh issue close <num> --comment \"Done. <commit-sha or reason>\" | |\n| * | |\n| * - FREI issues (no agent:* label) are visible to every agent's heartbeat. | |\n| * Any agent may claim them by adding their own label and a comment. | |\n| * | |\n| * ============================================================================= | |\n| * QUEUE DISCIPLINE (rules for AI agents, non-negotiable) | |\n| * ============================================================================= | |\n| * | |\n| * 1) idle != done. | |\n| * The heartbeat prints either \"idle, queue empty\" (genuinely done) or | |\n| * \"idle, N open tickets\" followed by PENDING lines (NOT done — those are | |\n| * your open assignments). When you see PENDING lines, your next action is | |\n| * to open the top ticket and work it, not report \"idle\". | |\n| * | |\n| * 2) Every ticket must be actively resolved. For each open ticket decide: | |\n| * (a) handle it yourself and close with a reason / commit SHA | |\n| * (b) re-route to the correct agent with a one-line comment | |\n| * (c) escalate to a human (label \"agent:human\") with a clear question | |\n| * Leaving a ticket untouched is not allowed. | |\n| * | |\n| * 3) Stuck? Cross-comment. If you are blocked on a ticket for >3 rounds | |\n| * without progress, look for thematically related tickets (same theme:* | |\n| * label) in other agents' queues and comment there. You may comment on any | |\n| * ticket. You may NOT take ownership (change the agent label) unless the | |\n| * ticket has had no activity for 7+ days — then claim it and explain why. | |\n| * | |\n| * ============================================================================= | |\n| * SELF-MUTE AFTER YOUR OWN POSTS (--ack flag) | |\n| * ============================================================================= | |\n| * | |\n| * Problem: this watcher cannot tell whether a new comment was written by YOU | |\n| * or by another agent, because all agents may share a single GitHub account | |\n| * (e.g. a bot account). So your own comment would trigger a spurious WAKE. | |\n| * | |\n| * Fix: after you post a comment or edit labels on an issue, run: | |\n| * | |\n| * node gh-agent-watch.js <your-name> --ack <issue-num> [<issue-num> ...] | |\n| * | |\n| * This re-baselines that issue's signature in the local state file so the next | |\n| * poll sees no diff. It exits immediately (no poll loop). | |\n| * | |\n| * ============================================================================= | |\n| * USAGE | |\n| * ============================================================================= | |\n| * | |\n| * # Start the watcher (run inside a persistent/Monitor process): | |\n| * node gh-agent-watch.js <agent-name> [--repo owner/repo] [--interval 60] | |\n| * | |\n| * # Acknowledge your own edits (self-mute, call after every gh post/edit): | |\n| * node gh-agent-watch.js <agent-name> --ack <num> [<num> ...] | |\n| * | |\n| * # Examples: | |\n| * node gh-agent-watch.js tester --repo myorg/myproject | |\n| * node gh-agent-watch.js implementer --repo myorg/myproject --interval 30 | |\n| * node gh-agent-watch.js tester --ack 42 43 | |\n| * | |\n| * Prerequisites: `gh` CLI installed and authenticated (`gh auth login`). | |\n| * | |\n| * ============================================================================= | |\n| * OUTPUT FORMAT (for orchestrators parsing stdout) | |\n| * ============================================================================= | |\n| * | |\n| * WAKE NEU #<n> <title> — new issue just appeared in your queue | |\n| * WAKE UPDATE #<n> <title> — existing issue was updated (new comment etc) | |\n| * PENDING #<n> <title> — heartbeat: open issue already in your queue | |\n| * FREI #<n> [<age>] <title> — unclaimed issue (no agent:* label) | |\n| * ROLLE: <agent> — <reminder> — heartbeat role reminder line | |\n| * [gh-agent-watch <agent>] ... — informational/error lines (not action signals) | |\n| * | |\n| * ============================================================================= | |\n| * SETUP IN 3 STEPS | |\n| * ============================================================================= | |\n| * | |\n| * Step 1 — Install gh CLI and authenticate: | |\n| * https://cli.github.com/ → gh auth login | |\n| * | |\n| * Step 2 — Create agent labels in your GitHub repo (once, by a human or script): | |\n| * gh label create \"agent:implementer\" --repo owner/repo --color \"0075ca\" | |\n| * gh label create \"agent:tester\" --repo owner/repo --color \"e4e669\" | |\n| * gh label create \"agent:reviewer\" --repo owner/repo --color \"d93f0b\" | |\n| * gh label create \"agent:docs\" --repo owner/repo --color \"0e8a16\" | |\n| * gh label create \"agent:human\" --repo owner/repo --color \"ffffff\" | |\n| * # Add more as needed. | |\n| * | |\n| * Step 3 — Start each agent's watcher in a persistent process: | |\n| * node gh-agent-watch.js implementer --repo owner/repo | |\n| * node gh-agent-watch.js tester --repo owner/repo | |\n| * # etc. | |\n| * | |\n| * State is stored in ~/.gh-agent-watch/<agent-name>.json (cross-session). | |\n| * Delete the file to reset seen-state (causes all open issues to fire as NEU). | |\n| * | |\n| * ============================================================================= | |\n| */ | |\n| const { execSync } = require('node:child_process'); | |\n| const fs = require('node:fs'); | |\n| const path = require('node:path'); | |\n| // --------------------------------------------------------------------------- | |\n| // Argument parsing | |\n| // --------------------------------------------------------------------------- | |\n| const args = process.argv.slice(2); | |\n| if (!args[0] || args[0].startsWith('-')) { | |\n| console.error('Usage: gh-agent-watch.js <agent-name> [--repo owner/repo] [--interval 60]'); | |\n| console.error(' gh-agent-watch.js <agent-name> --ack <num> [<num> ...]'); | |\n| console.error(''); | |\n| console.error('The agent-name must match the label suffix used in GitHub.'); | |\n| console.error('Example label: \"agent:tester\" → agent-name: \"tester\"'); | |\n| process.exit(2); | |\n| } | |\n| const agent = args[0]; | |\n| // --repo (default: tries to read from `gh repo view` in cwd, falls back to error) | |\n| const repoFlagIdx = args.indexOf('--repo'); | |\n| const repoFlagEq = (args.find(a => a.startsWith('--repo=')) || '').split('=')[1]; | |\n| let repo = repoFlagEq | |\n| || (repoFlagIdx >= 0 ? args[repoFlagIdx + 1] : null); | |\n| if (!repo) { | |\n| // Auto-detect from current working directory | |\n| try { | |\n| repo = execSync('gh repo view --json nameWithOwner -q .nameWithOwner', { | |\n| encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] | |\n| }).trim(); | |\n| } catch { | |\n| console.error('[gh-agent-watch] Could not detect repo. Pass --repo owner/repo explicitly.'); | |\n| process.exit(2); | |\n| } | |\n| } | |\n| // --interval in seconds (default 60) | |\n| const intervalFlagEq = (args.find(a => a.startsWith('--interval=')) || '').split('=')[1]; | |\n| const intervalFlagIdx = args.indexOf('--interval'); | |\n| const interval = Number( | |\n| intervalFlagEq | |\n| || (intervalFlagIdx >= 0 ? args[intervalFlagIdx + 1] : 60) | |\n| ) || 60; | |\n| // --ack <num> [<num> ...] | |\n| const ackIdx = args.indexOf('--ack'); | |\n| const ackNums = ackIdx >= 0 | |\n| ? args.slice(ackIdx + 1).filter(a => /^\\d+$/.test(a)).map(Number) | |\n| : []; | |\n| // --------------------------------------------------------------------------- | |\n| // State persistence (~/.gh-agent-watch/<agent>.json) | |\n| // --------------------------------------------------------------------------- | |\n| const stateDir = path.join(process.env.HOME || process.env.LOCALAPPDATA || '.', '.gh-agent-watch'); | |\n| fs.mkdirSync(stateDir, { recursive: true }); | |\n| const stateFile = path.join(stateDir, `${agent}.json`); | |\n| function loadState() { | |\n| try { return JSON.parse(fs.readFileSync(stateFile, 'utf8')); } | |\n| catch { return { seen: {} }; } | |\n| } | |\n| function saveState(s) { | |\n| fs.writeFileSync(stateFile, JSON.stringify(s, null, 2)); | |\n| } | |\n| // --------------------------------------------------------------------------- | |\n| // Poll FREI: open issues with no agent:* label (fall-through / unclaimed) | |\n| // These appear in every agent's heartbeat so nothing gets lost. | |\n| // --------------------------------------------------------------------------- | |\n| function pollFrei() { | |\n| try { | |\n| const raw = execSync( | |\n| `gh issue list --repo ${repo} --state open --limit 200 --json number,title,labels,createdAt`, | |\n| { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] } | |\n| ); | |\n| return JSON.parse(raw).filter(i => | |\n| !i.labels.some(l => l.name.startsWith('agent:')) | |\n| ); | |\n| } catch { | |\n| return []; | |\n| } | |\n| } | |\n| // --------------------------------------------------------------------------- | |\n| // Main poll: check for new or updated issues in this agent's queue | |\n| // --------------------------------------------------------------------------- | |\n| function poll() { | |\n| let issues; | |\n| try { | |\n| const raw = execSync( | |\n| `gh issue list --repo ${repo} --label \"agent:${agent}\" --state open --limit 200 ` | |\n| + `--json number,title,updatedAt,comments,createdAt`, | |\n| { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] } | |\n| ); | |\n| issues = JSON.parse(raw); | |\n| } catch (e) { | |\n| console.error(`[gh-agent-watch ${agent}] gh failed: ${e.message.split('\\n')[0]}`); | |\n| return; | |\n| } | |\n| const state = loadState(); | |\n| const seen = state.seen || {}; | |\n| let changed = 0; | |\n| for (const i of issues) { | |\n| const key = String(i.number); | |\n| // Signature: updatedAt + comment count. Any change fires a WAKE. | |\n| const sig = `${i.updatedAt}|${(i.comments || []).length}`; | |\n| if (seen[key] !== sig) { | |\n| const verb = seen[key] === undefined ? 'NEU' : 'UPDATE'; | |\n| console.log(`WAKE ${verb} #${i.number} ${i.title}`); | |\n| seen[key] = sig; | |\n| changed++; | |\n| } | |\n| } | |\n| state.seen = seen; | |\n| state.lastPoll = new Date().toISOString(); | |\n| saveState(state); | |\n| // Heartbeat — emitted every ~5 poll intervals when nothing changed. | |\n| // Tells the agent about its full open queue and any unclaimed issues. | |\n| if (changed === 0) { | |\n| const now = Date.now(); | |\n| const heartbeatDue = !state.heartbeat | |\n| || Date.now() - state.heartbeat > 5 * interval * 1000; | |\n| if (heartbeatDue) { | |\n| const lines = []; | |\n| if (issues.length === 0) { | |\n| lines.push(`[gh-agent-watch ${agent}] idle, queue empty`); | |\n| } else { | |\n| lines.push(`[gh-agent-watch ${agent}] idle, ${issues.length} open ticket(s) in queue:`); | |\n| // Cluster by age: NEU < 2h, MID 2-24h, ALT > 24h | |\n| const neu = [], mid = [], alt = []; | |\n| for (const i of issues) { | |\n| const ageH = (now - new Date(i.createdAt)) / 36e5; | |\n| if (ageH < 2) neu.push(i); | |\n| else if (ageH < 24) mid.push(i); | |\n| else alt.push(i); | |\n| } | |\n| const byUpdated = (a, b) => new Date(b.updatedAt) - new Date(a.updatedAt); | |\n| const fmt = (i) => `PENDING #${i.number} ${i.title}`; | |\n| if (neu.length > 0) { | |\n| lines.push('--- NEW (just assigned) ---'); | |\n| for (const i of neu.sort(byUpdated)) lines.push(fmt(i)); | |\n| } | |\n| for (const i of mid.sort(byUpdated)) lines.push(fmt(i)); | |\n| if (alt.length > 0) { | |\n| lines.push('--- STALE (> 24h in queue) ---'); | |\n| for (const i of alt.sort(byUpdated)) lines.push(fmt(i)); | |\n| } | |\n| } | |\n| // Unclaimed issues — visible across all agents' heartbeats | |\n| const frei = pollFrei(); | |\n| if (frei.length > 0) { | |\n| lines.push(` UNCLAIMED (no agent label): ${frei.length} open`); | |\n| for (const i of frei.slice(0, 5)) { | |\n| const ageH = Math.round((now - new Date(i.createdAt)) / 36e5); | |\n| const age = ageH < 2 ? 'new' : ageH < 24 ? `${ageH}h` : `${Math.round(ageH / 24)}d`; | |\n| lines.push(` FREI #${i.number} [${age}] ${i.title}`); | |\n| } | |\n| if (frei.length > 5) lines.push(` ... and ${frei.length - 5} more`); | |\n| } | |\n| for (const l of lines) console.log(l); | |\n| // Role reminder — nudges agent to act on PENDING tickets, not stay idle | |\n| console.log( | |\n| `ROLLE: ${agent} — read your agent self-doc (agenten/${agent}.md or equivalent). ` | |\n| + `idle != done: PENDING = your open assignments. ` | |\n| + `Consult specialist agents before escalating to human. ` | |\n| + `Check other agents' open tickets proactively for issues you can help with.` | |\n| ); | |\n| state.heartbeat = now; | |\n| saveState(state); | |\n| } | |\n| } | |\n| } | |\n| // --------------------------------------------------------------------------- | |\n| // --ack mode: re-baseline specific issues to suppress self-triggered wakes. | |\n| // | |\n| // WHY: If all agents post under the same GitHub account (e.g. a shared bot), | |\n| // the watcher cannot use viewerDidAuthor to distinguish \"I posted this\" from | |\n| // \"another agent posted this\". Without --ack, every comment you write would | |\n| // immediately trigger a WAKE UPDATE on your own issue. | |\n| // | |\n| // HOW: After posting a comment or editing labels, call: | |\n| // node gh-agent-watch.js <agent> --ack <issue-num> [...] | |\n| // This fetches the issue's current state, writes its signature into `seen`, | |\n| // and exits. The next regular poll finds no diff and stays silent. | |\n| // --------------------------------------------------------------------------- | |\n| function ack(nums) { | |\n| const state = loadState(); | |\n| const seen = state.seen || {}; | |\n| for (const num of nums) { | |\n| let raw; | |\n| try { | |\n| raw = execSync( | |\n| `gh issue view ${num} --repo ${repo} --json number,updatedAt,comments`, | |\n| { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] } | |\n| ); | |\n| } catch (e) { | |\n| console.error(`[gh-agent-watch ${agent}] --ack #${num}: gh failed: ${e.message.split('\\n')[0]}`); | |\n| continue; | |\n| } | |\n| const i = JSON.parse(raw); | |\n| const sig = `${i.updatedAt}|${(i.comments || []).length}`; | |\n| seen[String(i.number)] = sig; | |\n| console.log(`[gh-agent-watch ${agent}] ack #${i.number} -> ${sig}`); | |\n| } | |\n| state.seen = seen; | |\n| saveState(state); | |\n| } | |\n| // --------------------------------------------------------------------------- | |\n| // Entry point | |\n| // --------------------------------------------------------------------------- | |\n| if (ackNums.length > 0) { | |\n| ack(ackNums); | |\n| process.exit(0); | |\n| } | |\n| console.log(`[gh-agent-watch ${agent}] started — repo=${repo}, interval=${interval}s`); | |\n| console.log(`[gh-agent-watch ${agent}] watching label \"agent:${agent}\" for new/updated issues`); | |\n| console.log(`[gh-agent-watch ${agent}] state file: ${stateFile}`); | |\n| poll(); | |\n| setInterval(poll, interval * 1000); |\n\n# gh-agent-watch\n\nA zero-dependency GitHub Issue poller for multi-agent coding teams.\n\nEach AI agent runs one instance. It watches for issues labelled `agent:<name>`\n\nand prints a `WAKE`\n\nline to stdout whenever something new arrives. Your agent\n\norchestrator (Claude Code, a Monitor process, etc.) picks up that line and wakes\n\nthe agent to act.\n\n## Why this exists\n\nWhen multiple AI agents collaborate on a project, they need an inbox. GitHub\n\nIssues are a natural fit: they are persistent, auditable, and accessible from\n\nany terminal. This script gives each agent a continuous, low-noise feed of\n\nexactly the issues that are routed to them — nothing more.\n\n## Prerequisites\n\n- Node.js 18+\ninstalled and authenticated (`gh`\n\nCLI`gh auth login`\n\n)- A GitHub repo with\n`agent:*`\n\nlabels (see setup below)\n\n## Quickstart\n\n```\n# 1. Download the script (or copy it into your repo)\ncurl -O https://raw.githubusercontent.com/yourorg/yourrepo/main/gh-agent-watch.js\n\n# 2. Create agent labels in your repo (one-time, run as a human or admin script)\nREPO=yourorg/yourrepo\nfor role in implementer tester reviewer docs human; do\n  gh label create \"agent:$role\" --repo $REPO --color \"0075ca\" 2>/dev/null || true\ndone\n\n# 3. Start each agent's watcher in a persistent process\nnode gh-agent-watch.js implementer --repo yourorg/yourrepo\nnode gh-agent-watch.js tester      --repo yourorg/yourrepo\n# etc.\n```\n\nIf you run from inside the repo directory, `--repo`\n\nis auto-detected via\n\n`gh repo view`\n\n.\n\n## Agent roles (example team — rename freely)\n\n| Agent | What they do |\n|---|---|\n`implementer` |\nWrites and commits code; the only agent touching source files |\n`tester` |\nWrites test cases, user journeys, reports failures as issues |\n`reviewer` |\nCode review, architecture, technical quality gating |\n`docs` |\nUser-facing documentation; never modifies source code |\n`legal` |\nCompliance and regulatory gating before release |\n`market` |\nCompetitive analysis, domain model, product direction |\n`human` |\nEscalation target for decisions only a human should make |\n\nUse any names you like. The only constraint: the name must match the GitHub\n\nlabel suffix exactly. Label `agent:tester`\n\n→ agent name `tester`\n\n.\n\n## How routing works\n\nIssues move between agents by changing the `agent:*`\n\nlabel.\n\n```\n# Route an issue to another agent\ngh issue edit 42 --add-label \"agent:reviewer\" --remove-label \"agent:tester\"\n# Always add a one-line comment explaining why you are re-routing.\ngh issue comment 42 --body \"Routing to reviewer: needs architecture sign-off.\"\n\n# Create a new issue for another agent\ngh issue create --repo yourorg/yourrepo \\\n  --title \"Auth: token refresh missing on 401 (breaks mobile flow)\" \\\n  --label \"agent:implementer\" --label \"theme:auth\" \\\n  --body \"**Observed:** ...\\n**Why it matters:** ...\\n**Suggestion:** ...\"\n\n# Close a resolved issue\ngh issue close 42 --comment \"Fixed in commit abc1234.\"\n\n# Escalate to a human\ngh issue edit 42 --add-label \"agent:human\" --remove-label \"agent:tester\"\ngh issue comment 42 --body \"Needs product decision: which behaviour is correct?\"\n```\n\nUnclaimed issues (no `agent:*`\n\nlabel) appear in every agent's heartbeat as\n\n`FREI`\n\nlines. Any agent may claim them.\n\n## Self-mute after your own posts\n\nIf all agents post under the same GitHub account (e.g. a shared bot), the\n\nwatcher cannot distinguish your own comment from another agent's. Your post\n\nwould otherwise trigger a spurious `WAKE UPDATE`\n\n.\n\nFix: after every `gh issue comment`\n\nor `gh issue edit`\n\n, run:\n\n```\nnode gh-agent-watch.js tester --ack 42 43\n```\n\nThis re-baselines the issue's signature in local state and exits immediately.\n\n## Output format\n\n```\nWAKE NEU #42 Auth: token refresh missing on 401\nWAKE UPDATE #7 Docs: onboarding flow needs rewrite\nPENDING #12 Tests: checkout flow user journey incomplete\nFREI #99 [2h] Refactor: extract payment module\nROLLE: tester — read your agent self-doc ...\n```\n\n| Prefix | Meaning |\n|---|---|\n`WAKE NEU` |\nNew issue just appeared in your queue |\n`WAKE UPDATE` |\nExisting issue was updated (comment, label change, etc.) |\n`PENDING` |\nHeartbeat: this issue is already open in your queue |\n`FREI` |\nUnclaimed issue (no `agent:*` label) — anyone can take it |\n`ROLLE` |\nHeartbeat role reminder line |\n\n## State file\n\nSeen-state is stored in `~/.gh-agent-watch/<agent-name>.json`\n\n. Delete it to\n\nreset (all open issues will fire as `NEU`\n\non the next poll).\n\n## Options\n\n```\nnode gh-agent-watch.js <agent-name> [--repo owner/repo] [--interval 60]\nnode gh-agent-watch.js <agent-name> --ack <num> [<num> ...]\n```\n\n| Flag | Default | Description |\n|---|---|---|\n`--repo` |\nauto-detect via gh | GitHub repo in `owner/repo` format |\n`--interval` |\n60 | Poll interval in seconds |\n`--ack` |\n— | Re-baseline issues after your own edits |\n\n## Queue discipline for AI agents\n\nThese are not suggestions. An agent that ignores them blocks the whole team.\n\n-\n**idle != done.** Heartbeat prints`PENDING`\n\nlines for your open tickets.\n\nWhen you see them, act on them — do not report \"idle\". -\n**Every ticket gets resolved.** For each open ticket: handle it, re-route\n\nit (with a comment), or escalate to`agent:human`\n\n. Leaving it untouched is\n\nthe one forbidden option. -\n**Stuck? Cross-comment.** After 3+ rounds without progress, look for\n\nrelated tickets in other agents' queues (same`theme:*`\n\nlabel) and comment\n\nthere. You may comment anywhere. You may not change ownership unless the\n\nticket has had no activity for 7+ days.", "url": "https://wpnews.pro/news/a-zero-dependency-github-issue-poller-for-multi-agent-coding-teams", "canonical_source": "https://gist.github.com/atraining/d666e2d20e5abfdf92eb74a0d5f4918d", "published_at": "2026-05-27 10:50:50+00:00", "updated_at": "2026-05-27 11:17:54.837752+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-infrastructure", "ai-products", "artificial-intelligence"], "entities": ["GitHub"], "alternates": {"html": "https://wpnews.pro/news/a-zero-dependency-github-issue-poller-for-multi-agent-coding-teams", "markdown": "https://wpnews.pro/news/a-zero-dependency-github-issue-poller-for-multi-agent-coding-teams.md", "text": "https://wpnews.pro/news/a-zero-dependency-github-issue-poller-for-multi-agent-coding-teams.txt", "jsonld": "https://wpnews.pro/news/a-zero-dependency-github-issue-poller-for-multi-agent-coding-teams.jsonld"}}