feat: add disinto-factory skill for guided setup and operations
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
Distributable skill file (SKILL.md) that walks an AI agent through: - First-time factory setup with interactive [ASK] prompts - Post-init verification checklist - Mirror configuration to GitHub/Codeberg - Backlog seeding and issue creation - Ongoing monitoring: agent status, CI, PRs - Unsticking blocked issues Includes: - scripts/factory-status.sh — one-command factory health check - references/troubleshooting.md — common issues from real deployments - Slimmed CLAUDE.md pointing to the skill Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ed43f9db11
commit
cbe5df52b2
4 changed files with 318 additions and 237 deletions
248
CLAUDE.md
248
CLAUDE.md
|
|
@ -1,246 +1,20 @@
|
||||||
# CLAUDE.md — Skill file for disinto
|
# CLAUDE.md
|
||||||
|
|
||||||
## What is disinto?
|
This repo is **disinto** — an autonomous code factory.
|
||||||
|
|
||||||
Disinto is an autonomous code factory — bash scripts + Claude CLI that automate the full
|
For setup and operations, load the `disinto-factory` skill from `disinto-factory/SKILL.md`.
|
||||||
software development lifecycle: picking up issues, implementing via Claude, creating PRs,
|
|
||||||
running CI, reviewing, merging, and mirroring to external forges.
|
|
||||||
|
|
||||||
Read `VISION.md` for the project philosophy, `AGENTS.md` for architecture, and
|
Quick references:
|
||||||
`BOOTSTRAP.md` for setup instructions.
|
- `AGENTS.md` — per-agent architecture and file-level docs
|
||||||
|
- `VISION.md` — project philosophy
|
||||||
## Setting up a new factory instance
|
- `BOOTSTRAP.md` — detailed init walkthrough
|
||||||
|
- `disinto-factory/references/troubleshooting.md` — common issues and fixes
|
||||||
### Prerequisites
|
- `disinto-factory/scripts/factory-status.sh` — quick status check
|
||||||
|
|
||||||
- An LXD container (Debian 12) with Docker, git, jq, curl, tmux, python3 (>=3.11)
|
|
||||||
- `claude` CLI installed and authenticated
|
|
||||||
- SSH key for mirror pushes (added to GitHub/Codeberg)
|
|
||||||
|
|
||||||
### First-time setup
|
|
||||||
|
|
||||||
1. **Clone the repo** and cd into it:
|
|
||||||
```bash
|
|
||||||
git clone https://codeberg.org/johba/disinto.git && cd disinto
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Run init** against the repo you want the factory to develop:
|
|
||||||
```bash
|
|
||||||
bin/disinto init https://codeberg.org/org/repo --yes
|
|
||||||
```
|
|
||||||
For self-development (factory develops itself):
|
|
||||||
```bash
|
|
||||||
bin/disinto init https://codeberg.org/johba/disinto --yes --repo-root $(pwd)
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Verify the stack** came up:
|
|
||||||
```bash
|
|
||||||
docker ps --format "table {{.Names}}\t{{.Status}}"
|
|
||||||
```
|
|
||||||
Expected: forgejo (Up), woodpecker (healthy), woodpecker-agent (healthy), agents (Up),
|
|
||||||
edge (Up), staging (Up).
|
|
||||||
|
|
||||||
4. **Check WOODPECKER_TOKEN** was generated:
|
|
||||||
```bash
|
|
||||||
grep WOODPECKER_TOKEN .env
|
|
||||||
```
|
|
||||||
If empty, see "Known issues" below.
|
|
||||||
|
|
||||||
5. **Verify agent cron** is running:
|
|
||||||
```bash
|
|
||||||
docker exec -u agent disinto-agents-1 crontab -l -u agent
|
|
||||||
```
|
|
||||||
|
|
||||||
6. **Set up mirrors** (optional):
|
|
||||||
Edit `projects/<name>.toml`:
|
|
||||||
```toml
|
|
||||||
[mirrors]
|
|
||||||
github = "git@github.com:Org/repo.git"
|
|
||||||
codeberg = "git@codeberg.org:user/repo.git"
|
|
||||||
```
|
|
||||||
Ensure `~/.ssh` is mounted into the agents container and SSH keys are added
|
|
||||||
to the remote forges. The compose template includes the mount; just add your
|
|
||||||
public key to GitHub/Codeberg.
|
|
||||||
|
|
||||||
### Post-init checklist
|
|
||||||
|
|
||||||
- [ ] Stack containers all running and healthy
|
|
||||||
- [ ] `WOODPECKER_TOKEN` in `.env` is non-empty
|
|
||||||
- [ ] `projects/<name>.toml` exists with correct `repo_root` and `primary_branch`
|
|
||||||
- [ ] Labels exist on Forgejo repo: backlog, in-progress, blocked, tech-debt, etc.
|
|
||||||
- [ ] Agent container can reach Forgejo API: `docker exec disinto-agents-1 bash -c "source /home/agent/disinto/.env && curl -sf http://forgejo:3000/api/v1/version"`
|
|
||||||
- [ ] Agent repo is cloned: `docker exec -u agent disinto-agents-1 ls /home/agent/repos/<name>`
|
|
||||||
- If not: `docker exec disinto-agents-1 chown -R agent:agent /home/agent/repos && docker exec -u agent disinto-agents-1 bash -c "source /home/agent/disinto/.env && git clone http://dev-bot:\${FORGE_TOKEN}@forgejo:3000/org/repo.git /home/agent/repos/<name>"`
|
|
||||||
- [ ] Create backlog issues on Forgejo for the factory to work on
|
|
||||||
|
|
||||||
## Checking on the factory
|
|
||||||
|
|
||||||
### Agent status
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Are agents running?
|
|
||||||
docker exec disinto-agents-1 bash -c "
|
|
||||||
for f in /proc/[0-9]*/cmdline; do
|
|
||||||
cmd=\$(tr '\0' ' ' < \$f 2>/dev/null)
|
|
||||||
echo \$cmd | grep -qi claude && echo PID \$(echo \$f | cut -d/ -f3): running
|
|
||||||
done
|
|
||||||
"
|
|
||||||
|
|
||||||
# Latest dev-agent activity
|
|
||||||
docker exec disinto-agents-1 tail -20 /home/agent/data/logs/dev/dev-agent.log
|
|
||||||
|
|
||||||
# Latest poll activity
|
|
||||||
docker exec disinto-agents-1 tail -20 /home/agent/data/logs/dev/dev-agent-<project>.log
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue and PR status
|
|
||||||
|
|
||||||
```bash
|
|
||||||
source .env
|
|
||||||
# Open issues
|
|
||||||
curl -sf "http://localhost:3000/api/v1/repos/<org>/<repo>/issues?state=open" \
|
|
||||||
-H "Authorization: token $FORGE_TOKEN" | jq -r '.[] | "#\(.number) [\(.labels | map(.name) | join(","))] \(.title)"'
|
|
||||||
|
|
||||||
# Open PRs
|
|
||||||
curl -sf "http://localhost:3000/api/v1/repos/<org>/<repo>/pulls?state=open" \
|
|
||||||
-H "Authorization: token $FORGE_TOKEN" | jq -r '.[] | "PR #\(.number) [\(.head.ref)] \(.title)"'
|
|
||||||
```
|
|
||||||
|
|
||||||
### CI status
|
|
||||||
|
|
||||||
```bash
|
|
||||||
source .env
|
|
||||||
# Check pipelines (requires session cookie + CSRF for WP v3 API)
|
|
||||||
WP_CSRF=$(curl -sf -b "user_sess=$WOODPECKER_TOKEN" http://localhost:8000/web-config.js \
|
|
||||||
| sed -n 's/.*WOODPECKER_CSRF = "\([^"]*\)".*/\1/p')
|
|
||||||
curl -sf -b "user_sess=$WOODPECKER_TOKEN" -H "X-CSRF-Token: $WP_CSRF" \
|
|
||||||
"http://localhost:8000/api/repos/1/pipelines?page=1&per_page=5" \
|
|
||||||
| jq '.[] | {number, status, event}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Unsticking a blocked issue
|
|
||||||
|
|
||||||
When a dev-agent run fails (CI timeout, implementation error), the issue gets labeled
|
|
||||||
`blocked`. To retry:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
source .env
|
|
||||||
# 1. Close stale PR if any
|
|
||||||
curl -sf -X PATCH "http://localhost:3000/api/v1/repos/<org>/<repo>/pulls/<N>" \
|
|
||||||
-H "Authorization: token $FORGE_TOKEN" -H "Content-Type: application/json" \
|
|
||||||
-d '{"state":"closed"}'
|
|
||||||
|
|
||||||
# 2. Delete stale branch
|
|
||||||
curl -sf -X DELETE "http://localhost:3000/api/v1/repos/<org>/<repo>/branches/fix/issue-<N>" \
|
|
||||||
-H "Authorization: token $FORGE_TOKEN"
|
|
||||||
|
|
||||||
# 3. Remove locks
|
|
||||||
docker exec disinto-agents-1 rm -f /tmp/dev-agent-*.json /tmp/dev-agent-*.lock
|
|
||||||
|
|
||||||
# 4. Relabel issue to backlog
|
|
||||||
BACKLOG_ID=$(curl -sf "http://localhost:3000/api/v1/repos/<org>/<repo>/labels" \
|
|
||||||
-H "Authorization: token $FORGE_TOKEN" | jq -r '.[] | select(.name=="backlog") | .id')
|
|
||||||
curl -sf -X PUT "http://localhost:3000/api/v1/repos/<org>/<repo>/issues/<N>/labels" \
|
|
||||||
-H "Authorization: token $FORGE_TOKEN" -H "Content-Type: application/json" \
|
|
||||||
-d "{\"labels\":[$BACKLOG_ID]}"
|
|
||||||
|
|
||||||
# 5. Update agent repo to latest main
|
|
||||||
docker exec -u agent disinto-agents-1 bash -c \
|
|
||||||
"cd /home/agent/repos/<name> && git fetch origin && git reset --hard origin/main"
|
|
||||||
```
|
|
||||||
|
|
||||||
The next cron cycle (every 5 minutes) will pick it up.
|
|
||||||
|
|
||||||
### Triggering a poll manually
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker exec -u agent disinto-agents-1 bash -c \
|
|
||||||
"cd /home/agent/disinto && bash dev/dev-poll.sh projects/<name>.toml"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Filing issues
|
|
||||||
|
|
||||||
The factory picks up issues labeled `backlog`. The dev-agent:
|
|
||||||
1. Claims the issue (labels it `in-progress`)
|
|
||||||
2. Creates a worktree on branch `fix/issue-<N>`
|
|
||||||
3. Runs Claude to implement the fix
|
|
||||||
4. Pushes, creates a PR, waits for CI
|
|
||||||
5. Requests review from review-bot
|
|
||||||
6. Merges on approval, pushes to mirrors
|
|
||||||
|
|
||||||
Issue body should contain enough context for Claude to implement it. Include:
|
|
||||||
- What's wrong or what needs to change
|
|
||||||
- Which files are affected
|
|
||||||
- Any design constraints
|
|
||||||
- Dependency references: `Depends-on: #N` (dev-agent checks these before starting)
|
|
||||||
|
|
||||||
Use labels:
|
|
||||||
- `backlog` — ready for the dev-agent to pick up
|
|
||||||
- `blocked` — not ready (missing dependency, needs investigation)
|
|
||||||
- `in-progress` — claimed by dev-agent (set automatically)
|
|
||||||
- No label — parked, not for the factory to touch
|
|
||||||
|
|
||||||
## Reverse tunnel access (for browser UI)
|
|
||||||
|
|
||||||
If running in an LXD container with a reverse SSH tunnel to a jump host:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# On the LXD container, add to /etc/systemd/system/reverse-tunnel.service:
|
|
||||||
# -R 127.0.0.1:13000:localhost:3000 (Forgejo)
|
|
||||||
# -R 127.0.0.1:18000:localhost:8000 (Woodpecker)
|
|
||||||
|
|
||||||
# From your machine:
|
|
||||||
ssh -L 3000:localhost:13000 user@jump-host
|
|
||||||
# Then open http://localhost:3000 in your browser
|
|
||||||
```
|
|
||||||
|
|
||||||
Forgejo admin login: `disinto-admin` / set during init (or reset with
|
|
||||||
`docker exec disinto-forgejo-1 su -c "forgejo admin user change-password --username disinto-admin --password <pw> --must-change-password=false" git`).
|
|
||||||
|
|
||||||
## Known issues & workarounds
|
|
||||||
|
|
||||||
### WP CI agent needs host networking in LXD
|
|
||||||
|
|
||||||
Docker bridge networking inside LXD breaks gRPC/HTTP2. The compose template uses
|
|
||||||
`network_mode: host` + `privileged: true` for the WP agent, connecting via
|
|
||||||
`localhost:9000`. This is baked into the template and works on regular VMs too.
|
|
||||||
|
|
||||||
### CI step containers need Docker network
|
|
||||||
|
|
||||||
The WP agent spawns CI containers that need to reach Forgejo for git clone.
|
|
||||||
`WOODPECKER_BACKEND_DOCKER_NETWORK: disinto_disinto-net` is set in the compose
|
|
||||||
template to put CI containers on the compose network.
|
|
||||||
|
|
||||||
### Forgejo webhook allowlist
|
|
||||||
|
|
||||||
Forgejo blocks outgoing webhooks by default. The compose template sets
|
|
||||||
`FORGEJO__webhook__ALLOWED_HOST_LIST: "private"` to allow delivery to
|
|
||||||
Docker-internal hosts.
|
|
||||||
|
|
||||||
### OAuth2 token generation during init
|
|
||||||
|
|
||||||
The init script drives a Forgejo OAuth2 flow to generate a Woodpecker token.
|
|
||||||
This requires rewriting URL-encoded Docker-internal hostnames and submitting
|
|
||||||
all Forgejo grant form fields. If token generation fails, check Forgejo logs
|
|
||||||
for "Unregistered Redirect URI" errors.
|
|
||||||
|
|
||||||
### Woodpecker UI not accessible via tunnel
|
|
||||||
|
|
||||||
The WP OAuth login redirects use Docker-internal hostnames that browsers can't
|
|
||||||
resolve. Use the Forgejo UI instead — CI results appear as commit statuses on PRs.
|
|
||||||
|
|
||||||
### PROJECT_REPO_ROOT inside agents container
|
|
||||||
|
|
||||||
The agents container needs `PROJECT_REPO_ROOT` set in its environment to
|
|
||||||
`/home/agent/repos/<name>` (not the host path from the TOML). The compose
|
|
||||||
template includes this. If the agent fails with "cd: no such file or directory",
|
|
||||||
check this env var.
|
|
||||||
|
|
||||||
## Code conventions
|
## Code conventions
|
||||||
|
|
||||||
See `AGENTS.md` for per-file architecture docs and coding conventions.
|
|
||||||
Key principles:
|
|
||||||
- Bash for checks, AI for judgment
|
- Bash for checks, AI for judgment
|
||||||
- Zero LLM tokens when idle (cron checks are pure bash)
|
- Zero LLM tokens when idle (cron polls are pure bash)
|
||||||
- Fire-and-forget mirror pushes (never block the pipeline)
|
- Fire-and-forget mirror pushes (never block the pipeline)
|
||||||
- Issues are the unit of work; PRs are the delivery mechanism
|
- Issues are the unit of work; PRs are the delivery mechanism
|
||||||
|
- See `AGENTS.md` for per-file watermarks and coding conventions
|
||||||
|
|
|
||||||
210
disinto-factory/SKILL.md
Normal file
210
disinto-factory/SKILL.md
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
---
|
||||||
|
name: disinto-factory
|
||||||
|
description: Set up and operate a disinto autonomous code factory. Use when bootstrapping a new factory instance, checking on agents and CI, managing the backlog, or troubleshooting the stack.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Disinto Factory
|
||||||
|
|
||||||
|
You are helping the user set up and operate a **disinto autonomous code factory** — a system
|
||||||
|
of bash scripts and Claude CLI that automates the full development lifecycle: picking up
|
||||||
|
issues, implementing via Claude, creating PRs, running CI, reviewing, merging, and mirroring.
|
||||||
|
|
||||||
|
## First-time setup
|
||||||
|
|
||||||
|
Walk the user through these steps interactively. Ask questions where marked with [ASK].
|
||||||
|
|
||||||
|
### 1. Environment
|
||||||
|
|
||||||
|
[ASK] Where will the factory run? Options:
|
||||||
|
- **LXD container** (recommended for isolation) — need Debian 12, Docker, nesting enabled
|
||||||
|
- **Bare VM or server** — need Debian/Ubuntu with Docker
|
||||||
|
- **Existing container** — check prerequisites
|
||||||
|
|
||||||
|
Verify prerequisites:
|
||||||
|
```bash
|
||||||
|
docker --version && git --version && jq --version && curl --version && tmux -V && python3 --version && claude --version
|
||||||
|
```
|
||||||
|
|
||||||
|
Any missing tool — help the user install it before continuing.
|
||||||
|
|
||||||
|
### 2. Clone and init
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://codeberg.org/johba/disinto.git && cd disinto
|
||||||
|
```
|
||||||
|
|
||||||
|
[ASK] What repo should the factory develop? Options:
|
||||||
|
- **Itself** (self-development): `bin/disinto init https://codeberg.org/johba/disinto --yes --repo-root $(pwd)`
|
||||||
|
- **Another project**: `bin/disinto init <repo-url> --yes`
|
||||||
|
|
||||||
|
Run the init and watch for:
|
||||||
|
- All bot users created (dev-bot, review-bot, etc.)
|
||||||
|
- `WOODPECKER_TOKEN` generated and saved
|
||||||
|
- Stack containers all started
|
||||||
|
|
||||||
|
### 3. Post-init verification
|
||||||
|
|
||||||
|
Run this checklist — fix any failures before proceeding:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stack healthy?
|
||||||
|
docker ps --format "table {{.Names}}\t{{.Status}}"
|
||||||
|
# Expected: forgejo, woodpecker (healthy), woodpecker-agent (healthy), agents, edge, staging
|
||||||
|
|
||||||
|
# Token generated?
|
||||||
|
grep WOODPECKER_TOKEN .env | grep -v "^$" && echo "OK" || echo "MISSING — see references/troubleshooting.md"
|
||||||
|
|
||||||
|
# Agent cron active?
|
||||||
|
docker exec -u agent disinto-agents-1 crontab -l -u agent
|
||||||
|
|
||||||
|
# Agent can reach Forgejo?
|
||||||
|
docker exec disinto-agents-1 bash -c "source /home/agent/disinto/.env && curl -sf http://forgejo:3000/api/v1/version | jq .version"
|
||||||
|
|
||||||
|
# Agent repo cloned?
|
||||||
|
docker exec -u agent disinto-agents-1 ls /home/agent/repos/
|
||||||
|
```
|
||||||
|
|
||||||
|
If the agent repo is missing, clone it:
|
||||||
|
```bash
|
||||||
|
docker exec disinto-agents-1 chown -R agent:agent /home/agent/repos
|
||||||
|
docker exec -u agent disinto-agents-1 bash -c "source /home/agent/disinto/.env && git clone http://dev-bot:\${FORGE_TOKEN}@forgejo:3000/<org>/<repo>.git /home/agent/repos/<name>"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Mirrors (optional)
|
||||||
|
|
||||||
|
[ASK] Should the factory mirror to external forges? If yes, which?
|
||||||
|
- GitHub: need repo URL and SSH key added to GitHub account
|
||||||
|
- Codeberg: need repo URL and SSH key added to Codeberg account
|
||||||
|
|
||||||
|
Show the user their public key:
|
||||||
|
```bash
|
||||||
|
cat ~/.ssh/id_ed25519.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
Test SSH access:
|
||||||
|
```bash
|
||||||
|
ssh -T git@github.com 2>&1; ssh -T git@codeberg.org 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
If SSH host keys are missing: `ssh-keyscan github.com codeberg.org >> ~/.ssh/known_hosts 2>/dev/null`
|
||||||
|
|
||||||
|
Edit `projects/<name>.toml` to add mirrors:
|
||||||
|
```toml
|
||||||
|
[mirrors]
|
||||||
|
github = "git@github.com:Org/repo.git"
|
||||||
|
codeberg = "git@codeberg.org:user/repo.git"
|
||||||
|
```
|
||||||
|
|
||||||
|
Test with a manual push:
|
||||||
|
```bash
|
||||||
|
source .env && source lib/env.sh && export PROJECT_TOML=projects/<name>.toml && source lib/load-project.sh && source lib/mirrors.sh && mirror_push
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Seed the backlog
|
||||||
|
|
||||||
|
[ASK] What should the factory work on first? Brainstorm with the user.
|
||||||
|
|
||||||
|
Help them create issues on the local Forgejo. Each issue needs:
|
||||||
|
- A clear title prefixed with `fix:`, `feat:`, or `chore:`
|
||||||
|
- A body describing what to change, which files, and any constraints
|
||||||
|
- The `backlog` label (so the dev-agent picks it up)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source .env
|
||||||
|
BACKLOG_ID=$(curl -sf "http://localhost:3000/api/v1/repos/<org>/<repo>/labels" \
|
||||||
|
-H "Authorization: token $FORGE_TOKEN" | jq -r '.[] | select(.name=="backlog") | .id')
|
||||||
|
|
||||||
|
curl -sf -X POST "http://localhost:3000/api/v1/repos/<org>/<repo>/issues" \
|
||||||
|
-H "Authorization: token $FORGE_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"title\": \"<title>\", \"body\": \"<body>\", \"labels\": [$BACKLOG_ID]}"
|
||||||
|
```
|
||||||
|
|
||||||
|
For issues with dependencies, add `Depends-on: #N` in the body — the dev-agent checks
|
||||||
|
these before starting.
|
||||||
|
|
||||||
|
Use labels:
|
||||||
|
- `backlog` — ready for the dev-agent
|
||||||
|
- `blocked` — parked, not for the factory
|
||||||
|
- No label — tracked but not for autonomous work
|
||||||
|
|
||||||
|
### 6. Watch it work
|
||||||
|
|
||||||
|
The dev-agent polls every 5 minutes. Trigger manually to see it immediately:
|
||||||
|
```bash
|
||||||
|
docker exec -u agent disinto-agents-1 bash -c "cd /home/agent/disinto && bash dev/dev-poll.sh projects/<name>.toml"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then monitor:
|
||||||
|
```bash
|
||||||
|
# Watch the agent work
|
||||||
|
docker exec disinto-agents-1 tail -f /home/agent/data/logs/dev/dev-agent.log
|
||||||
|
|
||||||
|
# Check for Claude running
|
||||||
|
docker exec disinto-agents-1 bash -c "for f in /proc/[0-9]*/cmdline; do cmd=\$(tr '\0' ' ' < \$f 2>/dev/null); echo \$cmd | grep -q 'claude.*-p' && echo 'Claude is running'; done"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ongoing operations
|
||||||
|
|
||||||
|
### Check factory status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source .env
|
||||||
|
|
||||||
|
# Issues
|
||||||
|
curl -sf "http://localhost:3000/api/v1/repos/<org>/<repo>/issues?state=open" \
|
||||||
|
-H "Authorization: token $FORGE_TOKEN" \
|
||||||
|
| jq -r '.[] | "#\(.number) [\(.labels | map(.name) | join(","))] \(.title)"'
|
||||||
|
|
||||||
|
# PRs
|
||||||
|
curl -sf "http://localhost:3000/api/v1/repos/<org>/<repo>/pulls?state=open" \
|
||||||
|
-H "Authorization: token $FORGE_TOKEN" \
|
||||||
|
| jq -r '.[] | "PR #\(.number) [\(.head.ref)] \(.title)"'
|
||||||
|
|
||||||
|
# Agent logs
|
||||||
|
docker exec disinto-agents-1 tail -20 /home/agent/data/logs/dev/dev-agent.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check CI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source .env
|
||||||
|
WP_CSRF=$(curl -sf -b "user_sess=$WOODPECKER_TOKEN" http://localhost:8000/web-config.js \
|
||||||
|
| sed -n 's/.*WOODPECKER_CSRF = "\([^"]*\)".*/\1/p')
|
||||||
|
curl -sf -b "user_sess=$WOODPECKER_TOKEN" -H "X-CSRF-Token: $WP_CSRF" \
|
||||||
|
"http://localhost:8000/api/repos/1/pipelines?page=1&per_page=5" \
|
||||||
|
| jq '.[] | {number, status, event}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unstick a blocked issue
|
||||||
|
|
||||||
|
When a dev-agent run fails (CI timeout, implementation error), the issue gets labeled `blocked`:
|
||||||
|
|
||||||
|
1. Close stale PR and delete the branch
|
||||||
|
2. `docker exec disinto-agents-1 rm -f /tmp/dev-agent-*.json /tmp/dev-agent-*.lock`
|
||||||
|
3. Relabel the issue to `backlog`
|
||||||
|
4. Update agent repo: `docker exec -u agent disinto-agents-1 bash -c "cd /home/agent/repos/<name> && git fetch origin && git reset --hard origin/main"`
|
||||||
|
|
||||||
|
### Access Forgejo UI
|
||||||
|
|
||||||
|
If running in an LXD container with reverse tunnel:
|
||||||
|
```bash
|
||||||
|
# From your machine:
|
||||||
|
ssh -L 3000:localhost:13000 user@jump-host
|
||||||
|
# Open http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
Reset admin password if needed:
|
||||||
|
```bash
|
||||||
|
docker exec disinto-forgejo-1 su -c "forgejo admin user change-password --username disinto-admin --password <new-pw> --must-change-password=false" git
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important context
|
||||||
|
|
||||||
|
- Read `AGENTS.md` for per-agent architecture and file-level docs
|
||||||
|
- Read `VISION.md` for project philosophy
|
||||||
|
- Read `BOOTSTRAP.md` for detailed init walkthrough
|
||||||
|
- The factory uses a single internal Forgejo as its forge, regardless of where mirrors go
|
||||||
|
- Dev-agent uses `claude -p --resume` for session continuity across CI/review cycles
|
||||||
|
- Mirror pushes happen automatically after every merge (fire-and-forget)
|
||||||
|
- Cron schedule: dev-poll every 5min, review-poll every 5min, gardener 4x/day
|
||||||
53
disinto-factory/references/troubleshooting.md
Normal file
53
disinto-factory/references/troubleshooting.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
## WOODPECKER_TOKEN empty after init
|
||||||
|
|
||||||
|
The OAuth2 flow failed. Common causes:
|
||||||
|
|
||||||
|
1. **URL-encoded redirect_uri mismatch**: Forgejo logs show "Unregistered Redirect URI".
|
||||||
|
The init script must rewrite both plain and URL-encoded Docker hostnames.
|
||||||
|
|
||||||
|
2. **Forgejo must_change_password**: Admin user was created with forced password change.
|
||||||
|
The init script calls `--must-change-password=false` but Forgejo 11.x sometimes ignores it.
|
||||||
|
|
||||||
|
3. **WOODPECKER_OPEN not set**: WP refuses first-user OAuth registration without it.
|
||||||
|
|
||||||
|
Manual fix: reset admin password and re-run the token generation manually, or
|
||||||
|
use the Woodpecker UI to create a token.
|
||||||
|
|
||||||
|
## WP CI agent won't connect (DeadlineExceeded)
|
||||||
|
|
||||||
|
gRPC over Docker bridge fails in LXD (and possibly other nested container environments).
|
||||||
|
The compose template uses `network_mode: host` + `privileged: true` for the agent.
|
||||||
|
If you see this error, check:
|
||||||
|
- Server exposes port 9000: `grep "9000:9000" docker-compose.yml`
|
||||||
|
- Agent uses `localhost:9000`: `grep "WOODPECKER_SERVER" docker-compose.yml`
|
||||||
|
- Agent has `network_mode: host`
|
||||||
|
|
||||||
|
## CI clone fails (could not resolve host)
|
||||||
|
|
||||||
|
CI containers need to resolve Docker service names (e.g., `forgejo`).
|
||||||
|
Check `WOODPECKER_BACKEND_DOCKER_NETWORK` is set on the agent.
|
||||||
|
|
||||||
|
## Webhooks not delivered
|
||||||
|
|
||||||
|
Forgejo blocks outgoing webhooks by default. Check:
|
||||||
|
```bash
|
||||||
|
docker logs disinto-forgejo-1 2>&1 | grep "webhook.*ALLOWED_HOST_LIST"
|
||||||
|
```
|
||||||
|
Fix: add `FORGEJO__webhook__ALLOWED_HOST_LIST: "private"` to Forgejo environment.
|
||||||
|
|
||||||
|
Also verify the webhook exists:
|
||||||
|
```bash
|
||||||
|
curl -sf -u "disinto-admin:<password>" "http://localhost:3000/api/v1/repos/<org>/<repo>/hooks" | jq '.[].config.url'
|
||||||
|
```
|
||||||
|
If missing, deactivate and reactivate the repo in Woodpecker to auto-create it.
|
||||||
|
|
||||||
|
## Dev-agent fails with "cd: no such file or directory"
|
||||||
|
|
||||||
|
`PROJECT_REPO_ROOT` inside the agents container points to a host path that doesn't
|
||||||
|
exist in the container. Check the compose env:
|
||||||
|
```bash
|
||||||
|
docker inspect disinto-agents-1 --format '{{range .Config.Env}}{{println .}}{{end}}' | grep PROJECT_REPO_ROOT
|
||||||
|
```
|
||||||
|
Should be `/home/agent/repos/<name>`, not `/home/<user>/<name>`.
|
||||||
44
disinto-factory/scripts/factory-status.sh
Executable file
44
disinto-factory/scripts/factory-status.sh
Executable file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# factory-status.sh — Quick status check for a running disinto factory
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
FACTORY_ROOT="${1:-$(cd "$(dirname "$0")/../.." && pwd)}"
|
||||||
|
source "${FACTORY_ROOT}/.env" 2>/dev/null || { echo "No .env found at ${FACTORY_ROOT}"; exit 1; }
|
||||||
|
|
||||||
|
FORGE_URL="${FORGE_URL:-http://localhost:3000}"
|
||||||
|
REPO=$(grep '^repo ' "${FACTORY_ROOT}/projects/"*.toml 2>/dev/null | head -1 | sed 's/.*= *"//;s/"//')
|
||||||
|
[ -z "$REPO" ] && { echo "No project TOML found"; exit 1; }
|
||||||
|
|
||||||
|
echo "=== Stack ==="
|
||||||
|
docker ps --format "table {{.Names}}\t{{.Status}}" 2>/dev/null | grep disinto
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Open Issues ==="
|
||||||
|
curl -sf "${FORGE_URL}/api/v1/repos/${REPO}/issues?state=open&limit=20" \
|
||||||
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
||||||
|
| jq -r '.[] | "#\(.number) [\(.labels | map(.name) | join(","))] \(.title)"' 2>/dev/null || echo "(API error)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Open PRs ==="
|
||||||
|
curl -sf "${FORGE_URL}/api/v1/repos/${REPO}/pulls?state=open&limit=10" \
|
||||||
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
||||||
|
| jq -r '.[] | "PR #\(.number) [\(.head.ref)] \(.title)"' 2>/dev/null || echo "none"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Agent Activity ==="
|
||||||
|
docker exec disinto-agents-1 bash -c "tail -5 /home/agent/data/logs/dev/dev-agent.log 2>/dev/null" || echo "(no logs)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Claude Running? ==="
|
||||||
|
docker exec disinto-agents-1 bash -c "
|
||||||
|
found=false
|
||||||
|
for f in /proc/[0-9]*/cmdline; do
|
||||||
|
cmd=\$(tr '\0' ' ' < \"\$f\" 2>/dev/null)
|
||||||
|
if echo \"\$cmd\" | grep -q 'claude.*-p'; then found=true; echo 'Yes — Claude is actively working'; break; fi
|
||||||
|
done
|
||||||
|
\$found || echo 'No — idle'
|
||||||
|
" 2>/dev/null
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Mirrors ==="
|
||||||
|
cd "${FACTORY_ROOT}" 2>/dev/null && git remote -v | grep -E 'github|codeberg' | grep push || echo "none configured"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue