2026-03-12 12:44:15 +00:00
|
|
|
#!/usr/bin/env bash
|
2026-03-15 18:06:25 +01:00
|
|
|
# dev-poll.sh — Pull-based scheduler: find the next ready issue and start dev-agent
|
2026-03-12 12:44:15 +00:00
|
|
|
#
|
|
|
|
|
# Pull system: issues labeled "backlog" are candidates. An issue is READY when
|
2026-03-14 13:49:09 +01:00
|
|
|
# ALL its dependency issues are closed (and their PRs merged).
|
2026-03-12 12:44:15 +00:00
|
|
|
# No "todo" label needed — readiness is derived from reality.
|
|
|
|
|
#
|
|
|
|
|
# Priority:
|
|
|
|
|
# 1. Orphaned "in-progress" issues (agent died or PR needs attention)
|
2026-03-22 13:41:57 +00:00
|
|
|
# 2. Ready "priority" + "backlog" issues (FIFO within tier)
|
|
|
|
|
# 3. Ready "backlog" issues without "priority" (FIFO within tier)
|
2026-03-12 12:44:15 +00:00
|
|
|
#
|
refactor: split supervisor into infra + per-project, make poll scripts config-driven
Supervisor split (#26):
- Layer 1 (infra): P0 memory, P1 disk, P4 housekeeping — runs once, project-agnostic
- Layer 2 (per-project): P2 CI/dev-agent, P3 PRs/deps — iterates projects/*.toml
- Adding a new project requires only a new TOML file, no code changes
Poll scripts accept project TOML arg (#27):
- dev-poll.sh, review-poll.sh, gardener-poll.sh accept optional project TOML as $1
- env.sh loads PROJECT_TOML if set, overriding .env defaults
- Cron: `dev-poll.sh projects/versi.toml` targets that project
New files:
- lib/load-project.sh: TOML to env var loader (Python tomllib)
- projects/versi.toml: current project config extracted from .env
Backwards compatible: scripts without a TOML arg fall back to .env config.
Closes #26, Closes #27
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:57:18 +01:00
|
|
|
# Usage:
|
|
|
|
|
# cron every 10min
|
|
|
|
|
# dev-poll.sh [projects/harb.toml] # optional project config
|
2026-03-12 12:44:15 +00:00
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
refactor: split supervisor into infra + per-project, make poll scripts config-driven
Supervisor split (#26):
- Layer 1 (infra): P0 memory, P1 disk, P4 housekeeping — runs once, project-agnostic
- Layer 2 (per-project): P2 CI/dev-agent, P3 PRs/deps — iterates projects/*.toml
- Adding a new project requires only a new TOML file, no code changes
Poll scripts accept project TOML arg (#27):
- dev-poll.sh, review-poll.sh, gardener-poll.sh accept optional project TOML as $1
- env.sh loads PROJECT_TOML if set, overriding .env defaults
- Cron: `dev-poll.sh projects/versi.toml` targets that project
New files:
- lib/load-project.sh: TOML to env var loader (Python tomllib)
- projects/versi.toml: current project config extracted from .env
Backwards compatible: scripts without a TOML arg fall back to .env config.
Closes #26, Closes #27
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:57:18 +01:00
|
|
|
# Load shared environment (with optional project TOML override)
|
|
|
|
|
export PROJECT_TOML="${1:-}"
|
2026-03-12 12:44:15 +00:00
|
|
|
source "$(dirname "$0")/../lib/env.sh"
|
2026-03-18 02:05:54 +00:00
|
|
|
source "$(dirname "$0")/../lib/ci-helpers.sh"
|
2026-03-23 19:19:16 +00:00
|
|
|
# shellcheck source=../lib/mirrors.sh
|
|
|
|
|
source "$(dirname "$0")/../lib/mirrors.sh"
|
2026-03-23 21:46:59 +00:00
|
|
|
# shellcheck source=../lib/guard.sh
|
|
|
|
|
source "$(dirname "$0")/../lib/guard.sh"
|
|
|
|
|
check_active dev
|
2026-03-17 09:51:18 +00:00
|
|
|
|
2026-03-20 09:50:20 +00:00
|
|
|
# Gitea labels API requires []int64 — look up the "underspecified" label ID once
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
UNDERSPECIFIED_LABEL_ID=$(forge_api GET "/labels" 2>/dev/null \
|
2026-03-20 09:50:20 +00:00
|
|
|
| jq -r '.[] | select(.name == "underspecified") | .id' 2>/dev/null || true)
|
|
|
|
|
UNDERSPECIFIED_LABEL_ID="${UNDERSPECIFIED_LABEL_ID:-1300816}"
|
|
|
|
|
|
2026-03-17 13:15:49 +01:00
|
|
|
# Track CI fix attempts per PR to avoid infinite respawn loops
|
2026-03-21 14:25:13 +00:00
|
|
|
CI_FIX_TRACKER="${FACTORY_ROOT}/dev/ci-fixes-${PROJECT_NAME:-default}.json"
|
2026-03-18 06:30:17 +00:00
|
|
|
CI_FIX_LOCK="${CI_FIX_TRACKER}.lock"
|
2026-03-17 13:15:49 +01:00
|
|
|
ci_fix_count() {
|
|
|
|
|
local pr="$1"
|
2026-03-18 06:30:17 +00:00
|
|
|
flock "$CI_FIX_LOCK" python3 -c "import json,sys;d=json.load(open('$CI_FIX_TRACKER')) if __import__('os').path.exists('$CI_FIX_TRACKER') else {};print(d.get(str($pr),0))" 2>/dev/null || echo 0
|
2026-03-17 13:15:49 +01:00
|
|
|
}
|
|
|
|
|
ci_fix_increment() {
|
|
|
|
|
local pr="$1"
|
2026-03-18 06:30:17 +00:00
|
|
|
flock "$CI_FIX_LOCK" python3 -c "
|
2026-03-17 13:15:49 +01:00
|
|
|
import json,os
|
|
|
|
|
f='$CI_FIX_TRACKER'
|
|
|
|
|
d=json.load(open(f)) if os.path.exists(f) else {}
|
|
|
|
|
d[str($pr)]=d.get(str($pr),0)+1
|
|
|
|
|
json.dump(d,open(f,'w'))
|
|
|
|
|
" 2>/dev/null || true
|
|
|
|
|
}
|
|
|
|
|
ci_fix_reset() {
|
|
|
|
|
local pr="$1"
|
2026-03-18 06:30:17 +00:00
|
|
|
flock "$CI_FIX_LOCK" python3 -c "
|
2026-03-17 13:15:49 +01:00
|
|
|
import json,os
|
|
|
|
|
f='$CI_FIX_TRACKER'
|
|
|
|
|
d=json.load(open(f)) if os.path.exists(f) else {}
|
|
|
|
|
d.pop(str($pr),None)
|
|
|
|
|
json.dump(d,open(f,'w'))
|
|
|
|
|
" 2>/dev/null || true
|
|
|
|
|
}
|
2026-03-18 10:22:24 +00:00
|
|
|
ci_fix_check_and_increment() {
|
|
|
|
|
local pr="$1"
|
2026-03-18 11:44:30 +00:00
|
|
|
local check_only="${2:-}"
|
2026-03-18 10:22:24 +00:00
|
|
|
flock "$CI_FIX_LOCK" python3 -c "
|
|
|
|
|
import json,os
|
|
|
|
|
f='$CI_FIX_TRACKER'
|
2026-03-18 11:44:30 +00:00
|
|
|
check_only = '${check_only}' == 'check_only'
|
2026-03-18 10:22:24 +00:00
|
|
|
d=json.load(open(f)) if os.path.exists(f) else {}
|
|
|
|
|
count=d.get(str($pr),0)
|
2026-03-18 11:44:30 +00:00
|
|
|
if count>3:
|
2026-03-18 10:22:24 +00:00
|
|
|
print('exhausted:'+str(count))
|
2026-03-18 11:44:30 +00:00
|
|
|
elif count==3:
|
|
|
|
|
d[str($pr)]=4
|
|
|
|
|
json.dump(d,open(f,'w'))
|
|
|
|
|
print('exhausted_first_time:3')
|
|
|
|
|
elif check_only:
|
|
|
|
|
print('ok:'+str(count))
|
2026-03-18 10:22:24 +00:00
|
|
|
else:
|
|
|
|
|
count+=1
|
|
|
|
|
d[str($pr)]=count
|
|
|
|
|
json.dump(d,open(f,'w'))
|
|
|
|
|
print('ok:'+str(count))
|
|
|
|
|
" 2>/dev/null || echo "exhausted:99"
|
|
|
|
|
}
|
2026-03-17 13:15:49 +01:00
|
|
|
|
2026-03-21 04:18:43 +00:00
|
|
|
# Check whether an issue already has the "blocked" label
|
|
|
|
|
is_blocked() {
|
|
|
|
|
local issue="$1"
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
forge_api GET "/issues/${issue}/labels" 2>/dev/null \
|
2026-03-21 04:18:43 +00:00
|
|
|
| jq -e '.[] | select(.name == "blocked")' >/dev/null 2>&1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Post a CI-exhaustion diagnostic comment and label issue as blocked.
|
|
|
|
|
# Args: issue_num pr_num attempts
|
|
|
|
|
_post_ci_blocked_comment() {
|
|
|
|
|
local issue_num="$1" pr_num="$2" attempts="$3"
|
|
|
|
|
local blocked_id
|
2026-03-21 05:06:12 +00:00
|
|
|
blocked_id=$(ensure_blocked_label_id)
|
2026-03-21 04:18:43 +00:00
|
|
|
[ -z "$blocked_id" ] && return 0
|
|
|
|
|
|
|
|
|
|
local comment
|
|
|
|
|
comment="### Session failure diagnostic
|
|
|
|
|
|
|
|
|
|
| Field | Value |
|
|
|
|
|
|---|---|
|
|
|
|
|
| Exit reason | \`ci_exhausted_poll (${attempts} attempts)\` |
|
|
|
|
|
| Timestamp | \`$(date -u +%Y-%m-%dT%H:%M:%SZ)\` |
|
|
|
|
|
| PR | #${pr_num} |"
|
|
|
|
|
|
|
|
|
|
curl -sf -X POST \
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-21 04:18:43 +00:00
|
|
|
-H "Content-Type: application/json" \
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
"${FORGE_API}/issues/${issue_num}/comments" \
|
2026-03-21 04:18:43 +00:00
|
|
|
-d "$(jq -nc --arg b "$comment" '{body:$b}')" >/dev/null 2>&1 || true
|
|
|
|
|
curl -sf -X POST \
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-21 04:18:43 +00:00
|
|
|
-H "Content-Type: application/json" \
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
"${FORGE_API}/issues/${issue_num}/labels" \
|
2026-03-21 04:18:43 +00:00
|
|
|
-d "{\"labels\":[${blocked_id}]}" >/dev/null 2>&1 || true
|
2026-03-17 14:32:41 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 03:21:27 +00:00
|
|
|
# =============================================================================
|
2026-03-21 04:18:43 +00:00
|
|
|
# HELPER: handle CI-exhaustion check/block (DRY for 3 call sites)
|
2026-03-18 10:34:41 +00:00
|
|
|
# Sets CI_FIX_ATTEMPTS for caller use. Returns 0 if exhausted, 1 if not.
|
|
|
|
|
#
|
2026-03-18 11:44:30 +00:00
|
|
|
# Pass "check_only" as third arg for the backlog scan path: ok-counts are
|
|
|
|
|
# returned without incrementing (deferred to launch time so a WAITING_PRS
|
|
|
|
|
# exit cannot waste a fix attempt). The 3→4 sentinel bump is always atomic
|
2026-03-21 04:18:43 +00:00
|
|
|
# regardless of mode, preventing duplicate blocked labels from concurrent
|
2026-03-18 11:44:30 +00:00
|
|
|
# pollers.
|
2026-03-18 03:21:27 +00:00
|
|
|
# =============================================================================
|
|
|
|
|
handle_ci_exhaustion() {
|
2026-03-18 10:34:41 +00:00
|
|
|
local pr_num="$1" issue_num="$2"
|
|
|
|
|
local check_only="${3:-}"
|
|
|
|
|
local result
|
2026-03-18 10:22:24 +00:00
|
|
|
|
2026-03-21 04:18:43 +00:00
|
|
|
# Fast path: already blocked — skip without touching counter.
|
|
|
|
|
if is_blocked "$issue_num"; then
|
2026-03-18 10:22:24 +00:00
|
|
|
CI_FIX_ATTEMPTS=$(ci_fix_count "$pr_num")
|
2026-03-21 04:18:43 +00:00
|
|
|
log "PR #${pr_num} (issue #${issue_num}) already blocked (${CI_FIX_ATTEMPTS} attempts) — skipping"
|
2026-03-18 03:21:27 +00:00
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-18 11:44:30 +00:00
|
|
|
# Single flock-protected call: read + threshold-check + conditional bump.
|
|
|
|
|
# In check_only mode, ok-counts are returned without incrementing (deferred
|
|
|
|
|
# to launch time). In both modes, the 3→4 sentinel bump is atomic, so only
|
2026-03-21 04:18:43 +00:00
|
|
|
# one concurrent poller can ever receive exhausted_first_time:3 and label
|
|
|
|
|
# the issue blocked.
|
2026-03-18 11:44:30 +00:00
|
|
|
result=$(ci_fix_check_and_increment "$pr_num" "$check_only")
|
2026-03-18 10:22:24 +00:00
|
|
|
case "$result" in
|
|
|
|
|
ok:*)
|
|
|
|
|
CI_FIX_ATTEMPTS="${result#ok:}"
|
|
|
|
|
return 1
|
|
|
|
|
;;
|
2026-03-18 11:44:30 +00:00
|
|
|
exhausted_first_time:*)
|
|
|
|
|
CI_FIX_ATTEMPTS="${result#exhausted_first_time:}"
|
2026-03-21 04:18:43 +00:00
|
|
|
log "PR #${pr_num} (issue #${issue_num}) CI exhausted (${CI_FIX_ATTEMPTS} attempts) — marking blocked"
|
|
|
|
|
_post_ci_blocked_comment "$issue_num" "$pr_num" "$CI_FIX_ATTEMPTS"
|
2026-03-18 11:44:30 +00:00
|
|
|
;;
|
2026-03-18 10:22:24 +00:00
|
|
|
exhausted:*)
|
|
|
|
|
CI_FIX_ATTEMPTS="${result#exhausted:}"
|
2026-03-21 04:18:43 +00:00
|
|
|
log "PR #${pr_num} (issue #${issue_num}) CI exhausted (${CI_FIX_ATTEMPTS} attempts) — already blocked, skipping"
|
2026-03-18 10:22:24 +00:00
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
CI_FIX_ATTEMPTS=99
|
2026-03-21 04:18:43 +00:00
|
|
|
log "PR #${pr_num} (issue #${issue_num}) CI exhausted (${CI_FIX_ATTEMPTS} attempts) — already blocked, skipping"
|
2026-03-18 10:22:24 +00:00
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
return 0
|
2026-03-18 03:21:27 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-20 07:37:32 +00:00
|
|
|
# =============================================================================
|
|
|
|
|
# HELPER: merge an approved PR directly (no Claude needed)
|
|
|
|
|
#
|
|
|
|
|
# Merging an approved, CI-green PR is a single API call. Spawning dev-agent
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
# for this fails when the issue is already closed (forge auto-closes issues
|
2026-03-20 07:37:32 +00:00
|
|
|
# on PR creation when body contains "Fixes #N"), causing a respawn loop (#344).
|
|
|
|
|
# =============================================================================
|
|
|
|
|
try_direct_merge() {
|
|
|
|
|
local pr_num="$1" issue_num="$2"
|
|
|
|
|
|
|
|
|
|
log "PR #${pr_num} (issue #${issue_num}) approved + CI green → attempting direct merge"
|
|
|
|
|
|
|
|
|
|
local merge_resp merge_http
|
|
|
|
|
merge_resp=$(curl -sf -w '\n%{http_code}' -X POST \
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-20 07:37:32 +00:00
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
"${API}/pulls/${pr_num}/merge" \
|
|
|
|
|
-d '{"Do":"merge","delete_branch_after_merge":true}' 2>/dev/null) || true
|
|
|
|
|
|
|
|
|
|
merge_http=$(echo "$merge_resp" | tail -1)
|
|
|
|
|
|
|
|
|
|
if [ "${merge_http:-0}" = "200" ] || [ "${merge_http:-0}" = "204" ]; then
|
|
|
|
|
log "PR #${pr_num} merged successfully"
|
2026-03-22 09:12:04 +00:00
|
|
|
if [ "$issue_num" -gt 0 ]; then
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
# Close the issue (may already be closed by forge auto-close)
|
2026-03-22 09:12:04 +00:00
|
|
|
curl -sf -X PATCH \
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-22 09:12:04 +00:00
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
"${API}/issues/${issue_num}" \
|
|
|
|
|
-d '{"state":"closed"}' >/dev/null 2>&1 || true
|
|
|
|
|
# Remove in-progress label
|
|
|
|
|
curl -sf -X DELETE \
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-22 09:12:04 +00:00
|
|
|
"${API}/issues/${issue_num}/labels/in-progress" >/dev/null 2>&1 || true
|
|
|
|
|
# Clean up phase/session artifacts
|
|
|
|
|
rm -f "/tmp/dev-session-${PROJECT_NAME}-${issue_num}.phase" \
|
|
|
|
|
"/tmp/dev-impl-summary-${PROJECT_NAME}-${issue_num}.txt"
|
|
|
|
|
fi
|
2026-03-23 19:19:16 +00:00
|
|
|
# Pull merged primary branch and push to mirrors
|
|
|
|
|
git -C "${PROJECT_REPO_ROOT:-}" fetch origin "${PRIMARY_BRANCH:-}" 2>/dev/null || true
|
|
|
|
|
git -C "${PROJECT_REPO_ROOT:-}" checkout "${PRIMARY_BRANCH:-}" 2>/dev/null || true
|
|
|
|
|
git -C "${PROJECT_REPO_ROOT:-}" pull --ff-only origin "${PRIMARY_BRANCH:-}" 2>/dev/null || true
|
|
|
|
|
mirror_push
|
2026-03-20 07:37:32 +00:00
|
|
|
# Clean up CI fix tracker
|
|
|
|
|
ci_fix_reset "$pr_num"
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
log "PR #${pr_num} direct merge failed (HTTP ${merge_http:-?}) — falling back to dev-agent"
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
API="${FORGE_API}"
|
2026-03-21 14:25:13 +00:00
|
|
|
LOCKFILE="/tmp/dev-agent-${PROJECT_NAME:-default}.lock"
|
|
|
|
|
LOGFILE="${FACTORY_ROOT}/dev/dev-agent-${PROJECT_NAME:-default}.log"
|
2026-03-12 12:44:15 +00:00
|
|
|
PREFLIGHT_RESULT="/tmp/dev-agent-preflight.json"
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
|
|
|
|
|
log() {
|
|
|
|
|
printf '[%s] poll: %s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" "$*" >> "$LOGFILE"
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 06:46:26 +00:00
|
|
|
# =============================================================================
|
|
|
|
|
# PRE-LOCK: merge approved + CI-green PRs (no Claude session needed)
|
|
|
|
|
#
|
|
|
|
|
# Merging is a single API call — it doesn't need the dev-agent lock.
|
|
|
|
|
# This ensures approved PRs get merged even while a dev-agent is running.
|
|
|
|
|
# (See #531: direct merges should not be blocked by agent lock)
|
|
|
|
|
# =============================================================================
|
|
|
|
|
log "pre-lock: scanning for mergeable PRs"
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
PL_PRS=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-22 06:46:26 +00:00
|
|
|
"${API}/pulls?state=open&limit=20")
|
|
|
|
|
|
|
|
|
|
PL_MERGED_ANY=false
|
|
|
|
|
for i in $(seq 0 $(($(echo "$PL_PRS" | jq 'length') - 1))); do
|
|
|
|
|
PL_PR_NUM=$(echo "$PL_PRS" | jq -r ".[$i].number")
|
|
|
|
|
PL_PR_SHA=$(echo "$PL_PRS" | jq -r ".[$i].head.sha")
|
|
|
|
|
PL_PR_BRANCH=$(echo "$PL_PRS" | jq -r ".[$i].head.ref")
|
|
|
|
|
PL_PR_TITLE=$(echo "$PL_PRS" | jq -r ".[$i].title")
|
|
|
|
|
PL_PR_BODY=$(echo "$PL_PRS" | jq -r ".[$i].body // \"\"")
|
|
|
|
|
|
|
|
|
|
# Extract issue number from branch name, PR title, or PR body
|
|
|
|
|
PL_ISSUE=$(echo "$PL_PR_BRANCH" | grep -oP '(?<=fix/issue-)\d+' || true)
|
|
|
|
|
if [ -z "$PL_ISSUE" ]; then
|
|
|
|
|
PL_ISSUE=$(echo "$PL_PR_TITLE" | grep -oP '#\K\d+' | tail -1 || true)
|
|
|
|
|
fi
|
|
|
|
|
if [ -z "$PL_ISSUE" ]; then
|
|
|
|
|
PL_ISSUE=$(echo "$PL_PR_BODY" | grep -oiP '(?:closes|fixes|resolves)\s*#\K\d+' | head -1 || true)
|
|
|
|
|
fi
|
|
|
|
|
if [ -z "$PL_ISSUE" ]; then
|
2026-03-22 09:12:04 +00:00
|
|
|
# Allow chore PRs from gardener/planner/predictor to merge without issue number
|
|
|
|
|
if [[ "$PL_PR_BRANCH" =~ ^chore/(gardener|planner|predictor)- ]]; then
|
|
|
|
|
PL_ISSUE=0
|
|
|
|
|
else
|
|
|
|
|
continue
|
|
|
|
|
fi
|
2026-03-22 06:46:26 +00:00
|
|
|
fi
|
|
|
|
|
|
2026-03-23 17:19:01 +00:00
|
|
|
PL_CI_STATE=$(ci_commit_status "$PL_PR_SHA") || true
|
2026-03-22 06:46:26 +00:00
|
|
|
|
|
|
|
|
# Non-code PRs may have no CI — treat as passed
|
|
|
|
|
if ! ci_passed "$PL_CI_STATE" && ! ci_required_for_pr "$PL_PR_NUM"; then
|
|
|
|
|
PL_CI_STATE="success"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if ! ci_passed "$PL_CI_STATE"; then
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Check for approval (non-stale)
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
PL_REVIEWS=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-22 06:46:26 +00:00
|
|
|
"${API}/pulls/${PL_PR_NUM}/reviews") || true
|
|
|
|
|
PL_HAS_APPROVE=$(echo "$PL_REVIEWS" | \
|
|
|
|
|
jq -r '[.[] | select(.state == "APPROVED") | select(.stale == false)] | length') || true
|
|
|
|
|
|
|
|
|
|
if [ "${PL_HAS_APPROVE:-0}" -gt 0 ]; then
|
|
|
|
|
if try_direct_merge "$PL_PR_NUM" "$PL_ISSUE"; then
|
|
|
|
|
PL_MERGED_ANY=true
|
|
|
|
|
fi
|
|
|
|
|
# Direct merge failed — will fall through to post-lock dev-agent fallback
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
if [ "$PL_MERGED_ANY" = true ]; then
|
2026-03-22 06:52:39 +00:00
|
|
|
log "pre-lock: merged PR(s) successfully — exiting"
|
2026-03-22 06:46:26 +00:00
|
|
|
exit 0
|
|
|
|
|
fi
|
2026-03-22 06:52:39 +00:00
|
|
|
log "pre-lock: no PRs merged, checking agent lock"
|
2026-03-22 06:46:26 +00:00
|
|
|
|
2026-03-12 12:44:15 +00:00
|
|
|
# --- Check if dev-agent already running ---
|
|
|
|
|
if [ -f "$LOCKFILE" ]; then
|
|
|
|
|
LOCK_PID=$(cat "$LOCKFILE" 2>/dev/null || echo "")
|
|
|
|
|
if [ -n "$LOCK_PID" ] && kill -0 "$LOCK_PID" 2>/dev/null; then
|
|
|
|
|
log "agent running (PID ${LOCK_PID})"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
rm -f "$LOCKFILE"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# --- Memory guard ---
|
|
|
|
|
AVAIL_MB=$(awk '/MemAvailable/{printf "%d", $2/1024}' /proc/meminfo)
|
|
|
|
|
if [ "$AVAIL_MB" -lt 2000 ]; then
|
|
|
|
|
log "SKIP: only ${AVAIL_MB}MB available (need 2000MB)"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
# HELPER: check if a dependency issue is fully resolved (closed + PR merged)
|
|
|
|
|
# =============================================================================
|
|
|
|
|
dep_is_merged() {
|
|
|
|
|
local dep_num="$1"
|
|
|
|
|
|
|
|
|
|
# Check issue is closed
|
|
|
|
|
local dep_state
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
dep_state=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-12 12:44:15 +00:00
|
|
|
"${API}/issues/${dep_num}" | jq -r '.state // "open"')
|
|
|
|
|
if [ "$dep_state" != "closed" ]; then
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-15 18:06:25 +01:00
|
|
|
# Issue closed = dep satisfied. The scheduler only closes issues after
|
2026-03-13 11:25:35 +00:00
|
|
|
# merging, so closed state is trustworthy. No need to hunt for the
|
|
|
|
|
# specific PR — that was over-engineering that caused false negatives.
|
|
|
|
|
return 0
|
2026-03-12 12:44:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
# HELPER: extract dependency numbers from issue body
|
|
|
|
|
# =============================================================================
|
|
|
|
|
get_deps() {
|
|
|
|
|
local issue_body="$1"
|
2026-03-16 21:22:53 +01:00
|
|
|
# Shared parser: lib/parse-deps.sh (single source of truth)
|
|
|
|
|
echo "$issue_body" | bash "${FACTORY_ROOT}/lib/parse-deps.sh"
|
2026-03-12 12:44:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
# HELPER: check if issue is ready (all deps merged)
|
|
|
|
|
# =============================================================================
|
|
|
|
|
issue_is_ready() {
|
|
|
|
|
local issue_num="$1"
|
|
|
|
|
local issue_body="$2"
|
|
|
|
|
|
|
|
|
|
local deps
|
|
|
|
|
deps=$(get_deps "$issue_body")
|
|
|
|
|
|
|
|
|
|
if [ -z "$deps" ]; then
|
|
|
|
|
# No dependencies — always ready
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
while IFS= read -r dep; do
|
|
|
|
|
[ -z "$dep" ] && continue
|
|
|
|
|
if ! dep_is_merged "$dep"; then
|
|
|
|
|
log " #${issue_num} blocked: dep #${dep} not merged"
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
done <<< "$deps"
|
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
# PRIORITY 1: orphaned in-progress issues
|
|
|
|
|
# =============================================================================
|
|
|
|
|
log "checking for in-progress issues"
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
ORPHANS_JSON=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-12 12:44:15 +00:00
|
|
|
"${API}/issues?state=open&labels=in-progress&limit=10&type=issues")
|
|
|
|
|
|
|
|
|
|
ORPHAN_COUNT=$(echo "$ORPHANS_JSON" | jq 'length')
|
|
|
|
|
if [ "$ORPHAN_COUNT" -gt 0 ]; then
|
|
|
|
|
ISSUE_NUM=$(echo "$ORPHANS_JSON" | jq -r '.[0].number')
|
|
|
|
|
|
2026-03-18 06:41:43 +00:00
|
|
|
# Formula guard: formula-labeled issues should not be worked on by dev-agent.
|
|
|
|
|
# Remove in-progress label and skip to prevent infinite respawn cycle (#115).
|
|
|
|
|
ORPHAN_LABELS=$(echo "$ORPHANS_JSON" | jq -r '.[0].labels[].name' 2>/dev/null) || true
|
2026-03-25 17:16:13 +00:00
|
|
|
SKIP_LABEL=$(echo "$ORPHAN_LABELS" | grep -oE '^(formula|action|prediction/dismissed|prediction/unreviewed)$' | head -1) || true
|
2026-03-20 17:15:03 +00:00
|
|
|
if [ -n "$SKIP_LABEL" ]; then
|
|
|
|
|
log "issue #${ISSUE_NUM} has '${SKIP_LABEL}' label — removing in-progress, skipping"
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
curl -sf -X DELETE -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-18 06:41:43 +00:00
|
|
|
"${API}/issues/${ISSUE_NUM}/labels/in-progress" >/dev/null 2>&1 || true
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-12 12:44:15 +00:00
|
|
|
# Check if there's already an open PR for this issue
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
HAS_PR=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-12 12:44:15 +00:00
|
|
|
"${API}/pulls?state=open&limit=20" | \
|
|
|
|
|
jq -r --arg branch "fix/issue-${ISSUE_NUM}" \
|
|
|
|
|
'.[] | select(.head.ref == $branch) | .number' | head -1) || true
|
|
|
|
|
|
|
|
|
|
if [ -n "$HAS_PR" ]; then
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
PR_SHA=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-12 12:44:15 +00:00
|
|
|
"${API}/pulls/${HAS_PR}" | jq -r '.head.sha') || true
|
2026-03-23 17:19:01 +00:00
|
|
|
CI_STATE=$(ci_commit_status "$PR_SHA") || true
|
2026-03-12 12:44:15 +00:00
|
|
|
|
2026-03-19 13:48:00 +00:00
|
|
|
# Non-code PRs (docs, formulas, evidence) may have no CI — treat as passed
|
|
|
|
|
if ! ci_passed "$CI_STATE" && ! ci_required_for_pr "$HAS_PR"; then
|
|
|
|
|
CI_STATE="success"
|
|
|
|
|
log "PR #${HAS_PR} has no code files — treating CI as passed"
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-20 00:59:51 +00:00
|
|
|
# Check formal reviews (single fetch to avoid race window)
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
REVIEWS_JSON=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-20 00:59:51 +00:00
|
|
|
"${API}/pulls/${HAS_PR}/reviews") || true
|
|
|
|
|
HAS_APPROVE=$(echo "$REVIEWS_JSON" | \
|
2026-03-12 12:44:15 +00:00
|
|
|
jq -r '[.[] | select(.state == "APPROVED") | select(.stale == false)] | length') || true
|
2026-03-20 00:59:51 +00:00
|
|
|
HAS_CHANGES=$(echo "$REVIEWS_JSON" | \
|
2026-03-21 00:05:09 +00:00
|
|
|
jq -r '[.[] | select(.state == "REQUEST_CHANGES") | select(.stale == false)] | length') || true
|
2026-03-12 12:44:15 +00:00
|
|
|
|
2026-03-17 09:51:18 +00:00
|
|
|
if ci_passed "$CI_STATE" && [ "${HAS_APPROVE:-0}" -gt 0 ]; then
|
2026-03-20 07:37:32 +00:00
|
|
|
if try_direct_merge "$HAS_PR" "$ISSUE_NUM"; then
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
# Direct merge failed (conflicts?) — fall back to dev-agent
|
2026-03-20 11:13:21 +00:00
|
|
|
SESSION_NAME="dev-${PROJECT_NAME}-${ISSUE_NUM}"
|
|
|
|
|
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
|
|
|
|
log "issue #${ISSUE_NUM} already has active session ${SESSION_NAME} — skipping"
|
|
|
|
|
else
|
|
|
|
|
log "falling back to dev-agent for PR #${HAS_PR} merge"
|
|
|
|
|
nohup "${SCRIPT_DIR}/dev-agent.sh" "$ISSUE_NUM" >> "$LOGFILE" 2>&1 &
|
|
|
|
|
log "started dev-agent PID $! for issue #${ISSUE_NUM} (agent-merge)"
|
|
|
|
|
fi
|
2026-03-12 12:44:15 +00:00
|
|
|
exit 0
|
|
|
|
|
|
2026-03-18 21:03:53 +00:00
|
|
|
# Do NOT gate REQUEST_CHANGES on ci_passed: act immediately even if CI is
|
|
|
|
|
# pending/unknown. Definitive CI failure is handled by the elif below.
|
|
|
|
|
elif [ "${HAS_CHANGES:-0}" -gt 0 ] && { ci_passed "$CI_STATE" || [ "$CI_STATE" = "pending" ] || [ "$CI_STATE" = "unknown" ] || [ -z "$CI_STATE" ]; }; then
|
2026-03-20 10:55:34 +00:00
|
|
|
SESSION_NAME="dev-${PROJECT_NAME}-${ISSUE_NUM}"
|
|
|
|
|
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
|
|
|
|
log "issue #${ISSUE_NUM} already has active session ${SESSION_NAME} — skipping"
|
|
|
|
|
else
|
|
|
|
|
log "issue #${ISSUE_NUM} PR #${HAS_PR} has REQUEST_CHANGES — spawning agent"
|
|
|
|
|
nohup "${SCRIPT_DIR}/dev-agent.sh" "$ISSUE_NUM" >> "$LOGFILE" 2>&1 &
|
|
|
|
|
log "started dev-agent PID $! for issue #${ISSUE_NUM} (review fix)"
|
|
|
|
|
fi
|
2026-03-12 12:44:15 +00:00
|
|
|
exit 0
|
|
|
|
|
|
2026-03-21 06:10:39 +00:00
|
|
|
elif ci_failed "$CI_STATE"; then
|
2026-03-20 11:13:21 +00:00
|
|
|
SESSION_NAME="dev-${PROJECT_NAME}-${ISSUE_NUM}"
|
|
|
|
|
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
|
|
|
|
log "issue #${ISSUE_NUM} already has active session ${SESSION_NAME} — skipping"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
2026-03-21 11:22:07 +00:00
|
|
|
if handle_ci_exhaustion "$HAS_PR" "$ISSUE_NUM" "check_only"; then
|
2026-03-17 15:14:48 +01:00
|
|
|
# Fall through to backlog scan instead of exit
|
2026-03-18 03:21:27 +00:00
|
|
|
:
|
2026-03-17 13:15:49 +01:00
|
|
|
else
|
2026-03-21 11:22:07 +00:00
|
|
|
# Increment at actual launch time (not on guard-hit paths)
|
|
|
|
|
if handle_ci_exhaustion "$HAS_PR" "$ISSUE_NUM"; then
|
|
|
|
|
exit 0 # exhausted between check and launch
|
|
|
|
|
fi
|
2026-03-20 11:13:21 +00:00
|
|
|
log "issue #${ISSUE_NUM} PR #${HAS_PR} CI failed — spawning agent to fix (attempt ${CI_FIX_ATTEMPTS}/3)"
|
|
|
|
|
nohup "${SCRIPT_DIR}/dev-agent.sh" "$ISSUE_NUM" >> "$LOGFILE" 2>&1 &
|
|
|
|
|
log "started dev-agent PID $! for issue #${ISSUE_NUM} (CI fix)"
|
2026-03-17 15:14:48 +01:00
|
|
|
exit 0
|
2026-03-17 13:15:49 +01:00
|
|
|
fi
|
2026-03-12 12:44:15 +00:00
|
|
|
|
|
|
|
|
else
|
|
|
|
|
log "issue #${ISSUE_NUM} has open PR #${HAS_PR} (CI: ${CI_STATE}, waiting)"
|
|
|
|
|
fi
|
|
|
|
|
else
|
2026-03-20 11:13:21 +00:00
|
|
|
SESSION_NAME="dev-${PROJECT_NAME}-${ISSUE_NUM}"
|
|
|
|
|
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
|
|
|
|
log "issue #${ISSUE_NUM} already has active session ${SESSION_NAME} — skipping"
|
|
|
|
|
else
|
|
|
|
|
log "recovering orphaned issue #${ISSUE_NUM} (no PR found)"
|
|
|
|
|
nohup "${SCRIPT_DIR}/dev-agent.sh" "$ISSUE_NUM" >> "$LOGFILE" 2>&1 &
|
|
|
|
|
log "started dev-agent PID $! for issue #${ISSUE_NUM} (recovery)"
|
|
|
|
|
fi
|
2026-03-12 12:44:15 +00:00
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-14 07:34:47 +00:00
|
|
|
# =============================================================================
|
|
|
|
|
# PRIORITY 1.5: any open PR with REQUEST_CHANGES or CI failure (stuck PRs)
|
|
|
|
|
# =============================================================================
|
|
|
|
|
log "checking for stuck PRs"
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
OPEN_PRS=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-14 07:34:47 +00:00
|
|
|
"${API}/pulls?state=open&limit=20")
|
|
|
|
|
|
|
|
|
|
for i in $(seq 0 $(($(echo "$OPEN_PRS" | jq 'length') - 1))); do
|
|
|
|
|
PR_NUM=$(echo "$OPEN_PRS" | jq -r ".[$i].number")
|
|
|
|
|
PR_BRANCH=$(echo "$OPEN_PRS" | jq -r ".[$i].head.ref")
|
|
|
|
|
PR_SHA=$(echo "$OPEN_PRS" | jq -r ".[$i].head.sha")
|
|
|
|
|
|
2026-03-14 12:01:36 +00:00
|
|
|
# Extract issue number from branch name (fix/issue-NNN), PR title (#NNN), or PR body (Closes #NNN)
|
2026-03-14 11:12:27 +00:00
|
|
|
PR_TITLE=$(echo "$OPEN_PRS" | jq -r ".[$i].title")
|
2026-03-14 12:01:36 +00:00
|
|
|
PR_BODY=$(echo "$OPEN_PRS" | jq -r ".[$i].body // \"\"")
|
2026-03-14 07:34:47 +00:00
|
|
|
STUCK_ISSUE=$(echo "$PR_BRANCH" | grep -oP '(?<=fix/issue-)\d+' || true)
|
2026-03-14 11:12:27 +00:00
|
|
|
if [ -z "$STUCK_ISSUE" ]; then
|
|
|
|
|
STUCK_ISSUE=$(echo "$PR_TITLE" | grep -oP '#\K\d+' | tail -1 || true)
|
|
|
|
|
fi
|
2026-03-14 12:01:36 +00:00
|
|
|
if [ -z "$STUCK_ISSUE" ]; then
|
|
|
|
|
STUCK_ISSUE=$(echo "$PR_BODY" | grep -oiP '(?:closes|fixes|resolves)\s*#\K\d+' | head -1 || true)
|
|
|
|
|
fi
|
|
|
|
|
if [ -z "$STUCK_ISSUE" ]; then
|
2026-03-22 09:12:04 +00:00
|
|
|
# Allow chore PRs from gardener/planner/predictor to merge without issue number
|
|
|
|
|
if [[ "$PR_BRANCH" =~ ^chore/(gardener|planner|predictor)- ]]; then
|
|
|
|
|
STUCK_ISSUE=0
|
|
|
|
|
else
|
|
|
|
|
log "PR #${PR_NUM} has no issue ref — cannot spawn dev-agent, skipping"
|
|
|
|
|
continue
|
|
|
|
|
fi
|
2026-03-14 12:01:36 +00:00
|
|
|
fi
|
2026-03-14 07:34:47 +00:00
|
|
|
|
2026-03-23 17:19:01 +00:00
|
|
|
CI_STATE=$(ci_commit_status "$PR_SHA") || true
|
2026-03-19 13:48:00 +00:00
|
|
|
|
|
|
|
|
# Non-code PRs (docs, formulas, evidence) may have no CI — treat as passed
|
|
|
|
|
if ! ci_passed "$CI_STATE" && ! ci_required_for_pr "$PR_NUM"; then
|
|
|
|
|
CI_STATE="success"
|
|
|
|
|
log "PR #${PR_NUM} has no code files — treating CI as passed"
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-20 00:59:51 +00:00
|
|
|
# Single fetch to avoid race window between review checks
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
REVIEWS_JSON=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-20 00:59:51 +00:00
|
|
|
"${API}/pulls/${PR_NUM}/reviews") || true
|
|
|
|
|
HAS_CHANGES=$(echo "$REVIEWS_JSON" | \
|
2026-03-21 00:05:09 +00:00
|
|
|
jq -r '[.[] | select(.state == "REQUEST_CHANGES") | select(.stale == false)] | length') || true
|
2026-03-20 00:59:51 +00:00
|
|
|
HAS_APPROVE=$(echo "$REVIEWS_JSON" | \
|
2026-03-14 07:34:47 +00:00
|
|
|
jq -r '[.[] | select(.state == "APPROVED") | select(.stale == false)] | length') || true
|
|
|
|
|
|
2026-03-20 07:37:32 +00:00
|
|
|
# Merge directly if approved + CI green (no Claude needed — single API call)
|
2026-03-17 09:51:18 +00:00
|
|
|
if ci_passed "$CI_STATE" && [ "${HAS_APPROVE:-0}" -gt 0 ]; then
|
2026-03-20 07:37:32 +00:00
|
|
|
if try_direct_merge "$PR_NUM" "$STUCK_ISSUE"; then
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
2026-03-22 09:12:04 +00:00
|
|
|
# Direct merge failed — dev-agent fallback requires a real issue number
|
|
|
|
|
if [ "$STUCK_ISSUE" -eq 0 ]; then
|
|
|
|
|
log "PR #${PR_NUM} direct merge failed — no issue ref for dev-agent, skipping"
|
|
|
|
|
continue
|
|
|
|
|
fi
|
2026-03-20 07:37:32 +00:00
|
|
|
# Direct merge failed (conflicts?) — fall back to dev-agent
|
2026-03-20 11:13:21 +00:00
|
|
|
SESSION_NAME="dev-${PROJECT_NAME}-${STUCK_ISSUE}"
|
|
|
|
|
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
|
|
|
|
log "issue #${STUCK_ISSUE} already has active session ${SESSION_NAME} — skipping"
|
|
|
|
|
else
|
|
|
|
|
log "falling back to dev-agent for PR #${PR_NUM} merge"
|
|
|
|
|
nohup "${SCRIPT_DIR}/dev-agent.sh" "$STUCK_ISSUE" >> "$LOGFILE" 2>&1 &
|
|
|
|
|
log "started dev-agent PID $! for stuck PR #${PR_NUM} (agent-merge)"
|
|
|
|
|
fi
|
2026-03-18 17:59:36 +00:00
|
|
|
exit 0
|
2026-03-14 07:34:47 +00:00
|
|
|
fi
|
|
|
|
|
|
2026-03-22 09:12:04 +00:00
|
|
|
# Chore PRs without issue ref can only be direct-merged — skip dev-agent paths
|
|
|
|
|
if [ "$STUCK_ISSUE" -eq 0 ]; then
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-14 07:34:47 +00:00
|
|
|
# Stuck: REQUEST_CHANGES or CI failure → spawn agent
|
2026-03-18 20:50:56 +00:00
|
|
|
# Do NOT gate REQUEST_CHANGES on ci_passed: if a reviewer leaves REQUEST_CHANGES
|
|
|
|
|
# while CI is still pending/unknown, we must act immediately rather than wait for
|
|
|
|
|
# CI to settle. Definitive CI failure (non-pending, non-unknown) is handled by
|
|
|
|
|
# the elif below, so we only spawn here when CI has not definitively failed.
|
|
|
|
|
if [ "${HAS_CHANGES:-0}" -gt 0 ] && { ci_passed "$CI_STATE" || [ "$CI_STATE" = "pending" ] || [ "$CI_STATE" = "unknown" ] || [ -z "$CI_STATE" ]; }; then
|
2026-03-20 10:55:34 +00:00
|
|
|
SESSION_NAME="dev-${PROJECT_NAME}-${STUCK_ISSUE}"
|
|
|
|
|
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
|
|
|
|
log "issue #${STUCK_ISSUE} already has active session ${SESSION_NAME} — skipping"
|
2026-03-20 11:13:21 +00:00
|
|
|
continue
|
2026-03-20 10:55:34 +00:00
|
|
|
fi
|
2026-03-20 11:13:21 +00:00
|
|
|
log "PR #${PR_NUM} (issue #${STUCK_ISSUE}) has REQUEST_CHANGES — fixing first"
|
|
|
|
|
nohup "${SCRIPT_DIR}/dev-agent.sh" "$STUCK_ISSUE" >> "$LOGFILE" 2>&1 &
|
|
|
|
|
log "started dev-agent PID $! for stuck PR #${PR_NUM}"
|
2026-03-14 07:34:47 +00:00
|
|
|
exit 0
|
2026-03-21 06:10:39 +00:00
|
|
|
elif ci_failed "$CI_STATE"; then
|
2026-03-20 11:13:21 +00:00
|
|
|
SESSION_NAME="dev-${PROJECT_NAME}-${STUCK_ISSUE}"
|
|
|
|
|
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
|
|
|
|
log "issue #${STUCK_ISSUE} already has active session ${SESSION_NAME} — skipping"
|
|
|
|
|
continue
|
|
|
|
|
fi
|
2026-03-21 11:22:07 +00:00
|
|
|
if handle_ci_exhaustion "$PR_NUM" "$STUCK_ISSUE" "check_only"; then
|
2026-03-17 15:14:48 +01:00
|
|
|
continue # skip this PR, check next stuck PR or fall through to backlog
|
2026-03-17 13:15:49 +01:00
|
|
|
fi
|
2026-03-21 11:22:07 +00:00
|
|
|
# Increment at actual launch time (not on guard-hit paths)
|
|
|
|
|
if handle_ci_exhaustion "$PR_NUM" "$STUCK_ISSUE"; then
|
|
|
|
|
continue # exhausted between check and launch
|
|
|
|
|
fi
|
2026-03-20 11:13:21 +00:00
|
|
|
log "PR #${PR_NUM} (issue #${STUCK_ISSUE}) CI failed — fixing (attempt ${CI_FIX_ATTEMPTS}/3)"
|
|
|
|
|
nohup "${SCRIPT_DIR}/dev-agent.sh" "$STUCK_ISSUE" >> "$LOGFILE" 2>&1 &
|
|
|
|
|
log "started dev-agent PID $! for stuck PR #${PR_NUM}"
|
|
|
|
|
exit 0
|
2026-03-14 07:34:47 +00:00
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
2026-03-12 12:44:15 +00:00
|
|
|
# =============================================================================
|
|
|
|
|
# PRIORITY 2: find ready backlog issues (pull system)
|
2026-03-22 13:41:57 +00:00
|
|
|
#
|
|
|
|
|
# Two-tier pickup: priority+backlog issues first (FIFO), then plain backlog
|
|
|
|
|
# issues (FIFO). The "priority" label is added alongside "backlog", not instead.
|
2026-03-12 12:44:15 +00:00
|
|
|
# =============================================================================
|
|
|
|
|
log "scanning backlog for ready issues"
|
2026-03-22 13:41:57 +00:00
|
|
|
|
|
|
|
|
# Ensure the priority label exists on this repo
|
|
|
|
|
ensure_priority_label >/dev/null 2>&1 || true
|
|
|
|
|
|
|
|
|
|
# Tier 1: issues with both "priority" and "backlog" labels
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
PRIORITY_BACKLOG_JSON=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-22 13:41:57 +00:00
|
|
|
"${API}/issues?state=open&labels=priority,backlog&limit=20&type=issues&sort=oldest") || true
|
|
|
|
|
PRIORITY_BACKLOG_JSON="${PRIORITY_BACKLOG_JSON:-[]}"
|
|
|
|
|
|
|
|
|
|
# Tier 2: all "backlog" issues (includes priority ones — deduplicated below)
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
ALL_BACKLOG_JSON=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-20 08:33:03 +01:00
|
|
|
"${API}/issues?state=open&labels=backlog&limit=20&type=issues&sort=oldest")
|
2026-03-12 12:44:15 +00:00
|
|
|
|
2026-03-22 13:41:57 +00:00
|
|
|
# Combine: priority issues first, then remaining backlog issues (deduped)
|
|
|
|
|
BACKLOG_JSON=$(jq -n \
|
|
|
|
|
--argjson pri "$PRIORITY_BACKLOG_JSON" \
|
|
|
|
|
--argjson all "$ALL_BACKLOG_JSON" \
|
|
|
|
|
'($pri | map(.number)) as $pnums | $pri + [$all[] | select(.number as $n | $pnums | map(. == $n) | any | not)]')
|
|
|
|
|
|
2026-03-12 12:44:15 +00:00
|
|
|
BACKLOG_COUNT=$(echo "$BACKLOG_JSON" | jq 'length')
|
|
|
|
|
if [ "$BACKLOG_COUNT" -eq 0 ]; then
|
|
|
|
|
log "no backlog issues"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-22 13:41:57 +00:00
|
|
|
PRIORITY_COUNT=$(echo "$PRIORITY_BACKLOG_JSON" | jq 'length')
|
|
|
|
|
log "found ${BACKLOG_COUNT} backlog issues (${PRIORITY_COUNT} priority)"
|
2026-03-12 12:44:15 +00:00
|
|
|
|
|
|
|
|
# Check each for readiness
|
|
|
|
|
READY_ISSUE=""
|
|
|
|
|
for i in $(seq 0 $((BACKLOG_COUNT - 1))); do
|
|
|
|
|
ISSUE_NUM=$(echo "$BACKLOG_JSON" | jq -r ".[$i].number")
|
|
|
|
|
ISSUE_BODY=$(echo "$BACKLOG_JSON" | jq -r ".[$i].body // \"\"")
|
|
|
|
|
|
2026-03-18 09:49:44 +00:00
|
|
|
# Formula guard: formula-labeled issues must not be picked up by dev-agent.
|
|
|
|
|
# A formula issue that accidentally acquires the backlog label should be skipped.
|
|
|
|
|
ISSUE_LABELS=$(echo "$BACKLOG_JSON" | jq -r ".[$i].labels[].name" 2>/dev/null) || true
|
2026-03-25 17:16:13 +00:00
|
|
|
SKIP_LABEL=$(echo "$ISSUE_LABELS" | grep -oE '^(formula|action|prediction/dismissed|prediction/unreviewed)$' | head -1) || true
|
2026-03-20 17:15:03 +00:00
|
|
|
if [ -n "$SKIP_LABEL" ]; then
|
|
|
|
|
log "issue #${ISSUE_NUM} has '${SKIP_LABEL}' label — skipping in backlog scan"
|
2026-03-18 09:49:44 +00:00
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-12 12:44:15 +00:00
|
|
|
if ! issue_is_ready "$ISSUE_NUM" "$ISSUE_BODY"; then
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Check if there's already an open PR for this issue that needs attention
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
EXISTING_PR=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-12 12:44:15 +00:00
|
|
|
"${API}/pulls?state=open&limit=20" | \
|
|
|
|
|
jq -r --arg branch "fix/issue-${ISSUE_NUM}" --arg num "#${ISSUE_NUM}" \
|
|
|
|
|
'.[] | select((.head.ref == $branch) or (.title | contains($num))) | .number' | head -1) || true
|
|
|
|
|
|
|
|
|
|
if [ -n "$EXISTING_PR" ]; then
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
PR_SHA=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-12 12:44:15 +00:00
|
|
|
"${API}/pulls/${EXISTING_PR}" | jq -r '.head.sha') || true
|
2026-03-23 17:19:01 +00:00
|
|
|
CI_STATE=$(ci_commit_status "$PR_SHA") || true
|
2026-03-19 13:48:00 +00:00
|
|
|
|
|
|
|
|
# Non-code PRs (docs, formulas, evidence) may have no CI — treat as passed
|
|
|
|
|
if ! ci_passed "$CI_STATE" && ! ci_required_for_pr "$EXISTING_PR"; then
|
|
|
|
|
CI_STATE="success"
|
|
|
|
|
log "PR #${EXISTING_PR} has no code files — treating CI as passed"
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-20 00:59:51 +00:00
|
|
|
# Single fetch to avoid race window between review checks
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
REVIEWS_JSON=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-20 00:59:51 +00:00
|
|
|
"${API}/pulls/${EXISTING_PR}/reviews") || true
|
|
|
|
|
HAS_APPROVE=$(echo "$REVIEWS_JSON" | \
|
2026-03-12 12:44:15 +00:00
|
|
|
jq -r '[.[] | select(.state == "APPROVED") | select(.stale == false)] | length') || true
|
2026-03-20 00:59:51 +00:00
|
|
|
HAS_CHANGES=$(echo "$REVIEWS_JSON" | \
|
2026-03-21 00:05:09 +00:00
|
|
|
jq -r '[.[] | select(.state == "REQUEST_CHANGES") | select(.stale == false)] | length') || true
|
2026-03-12 12:44:15 +00:00
|
|
|
|
2026-03-17 09:51:18 +00:00
|
|
|
if ci_passed "$CI_STATE" && [ "${HAS_APPROVE:-0}" -gt 0 ]; then
|
2026-03-20 07:37:32 +00:00
|
|
|
if try_direct_merge "$EXISTING_PR" "$ISSUE_NUM"; then
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
# Direct merge failed (conflicts?) — fall back to dev-agent
|
|
|
|
|
log "falling back to dev-agent for PR #${EXISTING_PR} merge"
|
2026-03-18 17:59:36 +00:00
|
|
|
nohup "${SCRIPT_DIR}/dev-agent.sh" "$ISSUE_NUM" >> "$LOGFILE" 2>&1 &
|
|
|
|
|
log "started dev-agent PID $! for issue #${ISSUE_NUM} (agent-merge)"
|
|
|
|
|
exit 0
|
2026-03-12 12:44:15 +00:00
|
|
|
|
|
|
|
|
elif [ "${HAS_CHANGES:-0}" -gt 0 ]; then
|
|
|
|
|
log "#${ISSUE_NUM} PR #${EXISTING_PR} has REQUEST_CHANGES — picking up"
|
|
|
|
|
READY_ISSUE="$ISSUE_NUM"
|
|
|
|
|
break
|
|
|
|
|
|
2026-03-21 06:10:39 +00:00
|
|
|
elif ci_failed "$CI_STATE"; then
|
2026-03-18 10:34:41 +00:00
|
|
|
if handle_ci_exhaustion "$EXISTING_PR" "$ISSUE_NUM" "check_only"; then
|
2026-03-17 13:15:49 +01:00
|
|
|
# Don't add to WAITING_PRS — escalated PRs should not block new work
|
|
|
|
|
continue
|
|
|
|
|
fi
|
2026-03-18 10:34:41 +00:00
|
|
|
log "#${ISSUE_NUM} PR #${EXISTING_PR} CI failed — picking up (attempt $((CI_FIX_ATTEMPTS+1))/3)"
|
2026-03-12 12:44:15 +00:00
|
|
|
READY_ISSUE="$ISSUE_NUM"
|
2026-03-18 10:34:41 +00:00
|
|
|
READY_PR_FOR_INCREMENT="$EXISTING_PR"
|
2026-03-12 12:44:15 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
log "#${ISSUE_NUM} PR #${EXISTING_PR} exists (CI: ${CI_STATE}, waiting)"
|
2026-03-17 09:17:02 +00:00
|
|
|
WAITING_PRS="${WAITING_PRS:-}${WAITING_PRS:+, }#${EXISTING_PR}"
|
2026-03-12 12:44:15 +00:00
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
READY_ISSUE="$ISSUE_NUM"
|
|
|
|
|
log "#${ISSUE_NUM} is READY (all deps merged, no existing PR)"
|
|
|
|
|
break
|
|
|
|
|
done
|
|
|
|
|
|
2026-03-17 09:17:02 +00:00
|
|
|
# Single-threaded per project: if any issue has an open PR waiting for review/CI,
|
|
|
|
|
# don't start new work — let the pipeline drain first
|
|
|
|
|
if [ -n "$READY_ISSUE" ] && [ -n "${WAITING_PRS:-}" ]; then
|
|
|
|
|
log "holding #${READY_ISSUE} — waiting for open PR(s) to land first: ${WAITING_PRS}"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-12 12:44:15 +00:00
|
|
|
if [ -z "$READY_ISSUE" ]; then
|
|
|
|
|
log "no ready issues (all blocked by unmerged deps)"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
# LAUNCH: start dev-agent for the ready issue
|
|
|
|
|
# =============================================================================
|
2026-03-18 10:34:41 +00:00
|
|
|
# Deferred CI fix increment — only now that we're certain we are launching.
|
|
|
|
|
# Uses the atomic ci_fix_check_and_increment (inside handle_ci_exhaustion) so
|
|
|
|
|
# the counter is bumped exactly once even under concurrent poll invocations,
|
|
|
|
|
# and a WAITING_PRS exit above cannot silently consume a fix attempt.
|
|
|
|
|
if [ -n "${READY_PR_FOR_INCREMENT:-}" ]; then
|
|
|
|
|
if handle_ci_exhaustion "$READY_PR_FOR_INCREMENT" "$READY_ISSUE"; then
|
|
|
|
|
# exhausted (another poller incremented between scan and launch) — bail out
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-12 12:44:15 +00:00
|
|
|
log "launching dev-agent for #${READY_ISSUE}"
|
|
|
|
|
rm -f "$PREFLIGHT_RESULT"
|
|
|
|
|
|
|
|
|
|
nohup "${SCRIPT_DIR}/dev-agent.sh" "$READY_ISSUE" >> "$LOGFILE" 2>&1 &
|
|
|
|
|
AGENT_PID=$!
|
|
|
|
|
|
|
|
|
|
# Wait briefly for preflight (agent writes result before claiming)
|
2026-03-18 01:53:02 +00:00
|
|
|
for _w in $(seq 1 30); do
|
2026-03-12 12:44:15 +00:00
|
|
|
if [ -f "$PREFLIGHT_RESULT" ]; then
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
if ! kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
sleep 2
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
if [ -f "$PREFLIGHT_RESULT" ]; then
|
|
|
|
|
PREFLIGHT_STATUS=$(jq -r '.status // "unknown"' < "$PREFLIGHT_RESULT")
|
|
|
|
|
rm -f "$PREFLIGHT_RESULT"
|
|
|
|
|
|
|
|
|
|
case "$PREFLIGHT_STATUS" in
|
|
|
|
|
ready)
|
|
|
|
|
log "dev-agent running for #${READY_ISSUE}"
|
|
|
|
|
;;
|
|
|
|
|
unmet_dependency)
|
|
|
|
|
log "#${READY_ISSUE} has code-level dependency (preflight blocked)"
|
|
|
|
|
wait "$AGENT_PID" 2>/dev/null || true
|
|
|
|
|
;;
|
|
|
|
|
too_large)
|
|
|
|
|
REASON=$(jq -r '.reason // "unspecified"' < "$PREFLIGHT_RESULT" 2>/dev/null || echo "unspecified")
|
|
|
|
|
log "#${READY_ISSUE} too large: ${REASON}"
|
|
|
|
|
# Label as underspecified
|
fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker,
creates admin + bot users (dev-bot, review-bot), generates API
tokens, creates repo, and pushes code — all automated
- Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→
FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→
FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→
FORGE_BOT_USERNAMES (with backwards-compat fallbacks)
- Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all()
→forge_api_all() (with compat aliases)
- Add forge_url field to project TOML; load-project.sh derives
FORGE_API/FORGE_WEB from forge_url + repo
- Update parse_repo_slug() to accept any host URL, not just codeberg
- Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo)
- Update all 58 files: agent scripts, formulas, docs, site HTML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:57:12 +00:00
|
|
|
curl -sf -X POST -H "Authorization: token ${FORGE_TOKEN}" \
|
2026-03-12 12:44:15 +00:00
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
"${API}/issues/${READY_ISSUE}/labels" \
|
2026-03-20 09:50:20 +00:00
|
|
|
-d "{\"labels\":[${UNDERSPECIFIED_LABEL_ID}]}" >/dev/null 2>&1 || true
|
2026-03-12 12:44:15 +00:00
|
|
|
;;
|
|
|
|
|
already_done)
|
|
|
|
|
log "#${READY_ISSUE} already done"
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
log "#${READY_ISSUE} unknown preflight: ${PREFLIGHT_STATUS}"
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
elif kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
|
|
|
log "dev-agent running for #${READY_ISSUE} (passed preflight)"
|
|
|
|
|
else
|
|
|
|
|
log "dev-agent exited for #${READY_ISSUE} without preflight result"
|
|
|
|
|
fi
|