{"id":51778333,"url":"https://github.com/bborbe/github-dark-factory-agent","last_synced_at":"2026-07-20T08:32:15.014Z","repository":{"id":371036246,"uuid":"1298507682","full_name":"bborbe/github-dark-factory-agent","owner":"bborbe","description":"Cluster implementer for the dark-factory draft-PR pipeline: consumes github-dark-factory-watcher tasks, walks generate to approve to execute to verify on a draft PR branch, ends at human_review (never flips the PR)","archived":false,"fork":false,"pushed_at":"2026-07-15T19:37:10.000Z","size":221,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-07-20T08:32:13.869Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bborbe.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-12T17:59:21.000Z","updated_at":"2026-07-15T19:37:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bborbe/github-dark-factory-agent","commit_stats":null,"previous_names":["bborbe/github-dark-factory-agent"],"tags_count":1,"template":false,"template_full_name":"bborbe/agent-claude","purl":"pkg:github/bborbe/github-dark-factory-agent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgithub-dark-factory-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgithub-dark-factory-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgithub-dark-factory-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgithub-dark-factory-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bborbe","download_url":"https://codeload.github.com/bborbe/github-dark-factory-agent/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Fgithub-dark-factory-agent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35681006,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"ssl_error","status_checked_at":"2026-07-20T02:08:09.736Z","response_time":111,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-20T08:32:14.278Z","updated_at":"2026-07-20T08:32:15.002Z","avatar_url":"https://github.com/bborbe.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# github-dark-factory-agent\n\nThe cluster **dark-factory implementer**. A human drafts + approves a spec on a\ndraft-PR branch and walks away; this agent clones the branch and walks the\ndark-factory lifecycle (planning → execution → ai_review), landing the task at\n`phase: human_review` **without flipping the PR**. The human verifies + flips\ndraft→ready → the existing `github-pr-review-agent` merges.\n\nThree distinct phases (see `docs/design.md`):\n\n- **planning** — pure-Go: clone the draft-PR branch, validate preconditions\n  (ref == PR head, `.dark-factory.yaml` present, an approved-not-completed spec\n  in the PR diff, PR is a draft), write `## Plan`.\n- **execution** — drive the dark-factory lifecycle with `backend: local`\n  (Increment 2).\n- **ai_review** — read-only Claude verifier → `## Review`, route to\n  `human_review` (Increment 3).\n\n## How It Works\n\n1. Agent pipeline ([[task/controller]] → Kafka → [[task/executor]]) spawns a K8s Job with the `github-dark-factory-agent` image.\n2. The Job receives `TASK_CONTENT`, `TASK_ID`, `BRANCH`, `ALLOWED_TOOLS`, `MODEL`, etc. via env vars.\n3. `main.go` assembles the prompt via `lib/claude` (embedded `workflow.md` + `output-format.md` + task content).\n4. Runs `claude --print --output-format stream-json` with the allowed tools.\n5. Parses the JSON result and publishes to Kafka via `lib/delivery.KafkaResultDeliverer` (when `TASK_ID` set), or falls back to `NoopResultDeliverer` for local runs.\n\n## Env Vars\n\n| Var | Required | Default | Purpose |\n|---|---|---|---|\n| `TASK_CONTENT` | yes | — | Raw task markdown |\n| `BRANCH` | yes | — | `dev`/`prod` — used as Kafka topic prefix |\n| `TASK_ID` | no | — | Required when publishing results via Kafka |\n| `MODEL` | no | `sonnet` | `sonnet` or `opus` |\n| `ALLOWED_TOOLS` | no | — | Comma-separated Claude tool allowlist (e.g. `Read,Grep,Bash`) |\n| `AGENT_DIR` | no | `agent` | Directory containing `.claude/CLAUDE.md` guardrails |\n| `CLAUDE_CONFIG_DIR` | no | — | Claude Code OAuth config directory (PVC mount) |\n| `ENV_CONTEXT` | no | — | Comma-separated `KEY=VAL` pairs injected into the prompt |\n| `CLAUDE_ENV` | no | — | Comma-separated `KEY=VAL` pairs passed to the Claude CLI subprocess |\n| `KAFKA_BROKERS` | no | — | Required when `TASK_ID` is set |\n| `SENTRY_DSN` | no | — | Error reporting |\n| `GH_TOKEN` | no | — | Raw GitHub token; used as the clone/push credential when App creds are unset (local fallback) |\n| `APP_ID` | no | — | GitHub App ID (numeric); enables App auth when set with `INSTALLATION_ID` + a PEM |\n| `INSTALLATION_ID` | no | — | GitHub App Installation ID (numeric) |\n| `PEM_KEY_FILE` | no | — | Path to the GitHub App private key (PEM file mounted from a k8s Secret) |\n| `PEM_KEY` | no | — | GitHub App private key (PEM) as env content; mutually exclusive with `PEM_KEY_FILE` |\n\n## Creating a New Agent\n\nTo add a domain-specific agent that reuses this binary:\n\n1. Create a task file in OpenClaw vault with `assignee: claude-agent` (or a new assignee routed to this image via a Config CRD).\n2. Mount a PVC or Secret containing the domain-specific `.claude/CLAUDE.md` and any API credentials.\n3. Set `ALLOWED_TOOLS` on the Config CRD to the minimum tools the agent needs.\n4. Set `ENV_CONTEXT` to inject domain context (e.g. API URLs) into the prompt without modifying the binary.\n\n### Config CRD env pattern\n\nThe `Config` CRD's `spec.env` map becomes pod env vars, which `main.go` consumes via struct tags. Example from `k8s/github-dark-factory-agent.yaml`:\n\n```yaml\nspec:\n  env:\n    ALLOWED_TOOLS: WebSearch,WebFetch,Read,Grep\n```\n\nTune `ALLOWED_TOOLS` per task shape (minimum viable set):\n\n| Task shape | Minimum tools |\n|---|---|\n| Web research | `WebSearch,WebFetch,Read,Grep` |\n| Vault I/O via scripts | `Bash(scripts/vault-read.sh:*),Bash(scripts/vault-write.sh:*),Bash(scripts/vault-list.sh:*),Grep` |\n| API query via script | `Bash(scripts/trading-api-read.sh:*),Grep` |\n| Code edit | `Read,Write,Edit,Grep,Glob,Bash(go:*),Bash(make:*)` |\n\nPrefer constrained `Bash(path:*)` forms over bare `Bash` to minimize shell attack surface.\n\n### Claude subprocess env allowlist\n\n`lib/claude/claude-runner.go` strips pod env down to a safe allowlist (`HOME,PATH,USER,TZ,...`) before spawning `claude`. Custom env vars (API URLs, credentials) **must** be threaded explicitly via `ClaudeRunnerConfig.Env map[string]string` in `main.go`. Don't expect pod env to reach Claude by default. See `docs/` for precedent (trade-analysis commit `1ccfa674cf`).\n\n## Local Quick Test\n\n```bash\ncd ~/Documents/workspaces/agent/agent/claude\ngo run . \\\n  --task-content \"$(cat /path/to/task.md)\" \\\n  --model sonnet \\\n  --allowed-tools \"Read,Write,Edit,Bash,Grep,Glob\" \\\n  --agent-dir agent \\\n  --branch dev\n```\n\nSkips K8s, task controller, task executor, git writeback. Useful for iterating on prompts.\n\n## Links\n\nAdmin endpoints:\n- Dev: \u003chttps://dev.quant.benjamin-borbe.de/admin/github-dark-factory-agent/setloglevel/3\u003e\n- Prod: \u003chttps://prod.quant.benjamin-borbe.de/admin/github-dark-factory-agent/setloglevel/3\u003e\n\n## Related\n\n- `pkg/prompts/` — embedded prompts (`workflow.md`, `output-format.md`)\n- `agent/.claude/CLAUDE.md` — default agent guardrails\n- `docs/claude-oauth-setup.md` — seed PVC with Claude Code OAuth credentials\n- `lib/claude/` — shared prompt assembly + Claude CLI invocation\n- `lib/delivery/` — shared Kafka result publishing\n- `task/controller/` — Obsidian→Kafka event source\n- `task/executor/` — Kafka→K8s Job spawner\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbborbe%2Fgithub-dark-factory-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbborbe%2Fgithub-dark-factory-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbborbe%2Fgithub-dark-factory-agent/lists"}