- supervisor: skip *.done.jsonl in escalation glob (bug: wildcard matched
harb.done.jsonl producing spurious 'pending' log noise every cycle)
- supervisor: use wc -l instead of grep -c . for line counting (style nit)
- supervisor: consume gardener-esc-resolved.log via fixed() so escalation
resolutions appear in end-of-cycle supervisor reporting
- dev-poll: update all 'escalated to supervisor' log/matrix strings to
'escalated to gardener' (lines 263, 268, 344, 420)
- gardener: track _esc_total_created across all escalation entries and
write count to supervisor/gardener-esc-resolved.log after processing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- dev-poll.sh: write escalations to per-project files
(supervisor/escalations-{PROJECT_NAME}.jsonl) and add "project" field
so each project's escalations are isolated; update is_escalated() to
read from the same per-project paths
- gardener-poll.sh: add escalation processing block that reads the
per-project escalation file, fetches CI logs via Woodpecker, and
creates per-file ShellCheck sub-issues or generic CI failure issues
labeled backlog — runs with the correct CODEBERG_API and
WOODPECKER_REPO_ID already loaded from the project TOML
- supervisor-poll.sh: remove the escalation processing block; replace
with a simple flog report counting pending escalations per project
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The wait-for-CI loop sleeps 30s × 60 iterations waiting for CI
to report. Projects with WOODPECKER_REPO_ID=0 never get a status,
so the agent times out after 30min without merging approved PRs.
Now detects no-CI early and treats as success immediately.
- Race condition: mv escalations.jsonl to a PID-stamped snapshot before
processing so concurrent dev-poll appends go to a fresh file; rm snapshot
after loop — no entries are ever silently dropped
- SQL injection: validate ESC_PR_SHA is a 40-char hex string before
interpolating into the wpdb query
- sc_codes scope: compute per-file from file_errors (already filtered to
that file) instead of the entire step log; also switch grep to -F so
dots in filenames are not treated as regex wildcards
- step_pid validation: reject non-integer values from Woodpecker API before
passing as CLI argument
- Fallback body now distinguishes "CI logs unavailable" from "logs found
but issue creation API calls failed"
- ESC_GENERIC_FAIL: avoid leading blank line by using conditional separator
and fix code-block opening newline
- is_escalated(): remove dead esc_file/done_file locals; add Python-level
int() guard so empty/non-numeric issue or pr values fail cleanly instead
of producing a syntax error suppressed by 2>/dev/null
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- supervisor-poll.sh: replace P3 escalation log with actionable sub-issue creation.
For each entry in escalations.jsonl: fetch CI logs via woodpecker-cli, create one
sub-issue per file for ShellCheck failures, one combined issue for other CI failures,
or a fallback investigation issue if logs are unavailable. Move processed entries to
escalations.done.jsonl and clear escalations.jsonl.
- dev-poll.sh: add is_escalated() helper that checks both escalations.jsonl and
escalations.done.jsonl; use it (alongside ci_fix_count >= 3) in all three CI-fix
spawn paths so escalated PRs are skipped even if the ci-fixes tracker is reset.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs after #53 merged:
1. Escalation written every poll cycle (4 entries in 30min) — now writes once, bumps counter to 4 to skip
2. Exit after escalation blocked backlog work — now falls through to pick up next issue
Co-authored-by: openhands <openhands@all-hands.dev>
Reviewed-on: https://codeberg.org/johba/disinto/pulls/59
Reviewed-by: review_bot <review_bot@noreply.codeberg.org>
Dev-poll spawned a fresh agent every 10min for CI failures. Each agent started with CI_FIX_COUNT=0 — infinite loop.
Now tracks attempts per PR in `/tmp/dev-poll-ci-fixes-{project}.json`. After 3 failed rounds:
- Writes escalation to `supervisor/escalations.jsonl`
- Sends Matrix alert
- Stops respawning
Part of #52 (supervisor escalation pipeline).
Co-authored-by: openhands <openhands@all-hands.dev>
Reviewed-on: https://codeberg.org/johba/disinto/pulls/53
Reviewed-by: review_bot <review_bot@noreply.codeberg.org>
dev-poll.sh had 5 places checking CI_STATE='success', all blocking
projects without CI. Extracted ci_passed() helper that treats
empty/pending/unknown as pass when WOODPECKER_REPO_ID=0.
Don't start new issues while open PRs are waiting for review/CI.
This prevents dev-agent from churning through backlog issues
without reviews landing first.
TMPDIR is not guaranteed to be set. Replaced with /tmp/ directly.
This caused harb dev-agent to crash when posting refusal comments,
leaving issues stuck in a retry loop.
Hardcoded /tmp/dev-agent.lock meant harb and disinto dev-polls shared
a lock — one project's running agent blocked the other. Now uses
/tmp/dev-agent-{project}.lock and dev-agent-{project}.log.
The broad regex `(?:^|\n)\s*-\s*#\K[0-9]+` matched ANY bullet with #NNN,
including ## Related sections. This caused #893 (and likely others) to be
permanently blocked by sibling issues that aren't actual dependencies.
Now only extracts deps from:
- Inline 'depends on #NNN' / 'blocked by #NNN' phrases
- ## Dependencies / ## Depends on / ## Blocked by sections
This matches the same logic used by dev-poll.sh get_deps().
Replace lib/parse-deps.py with lib/parse-deps.sh to keep the toolchain
all-bash. Rewrite supervisor P3b cycle detection and P3c stale dep check
as pure bash using associative arrays and DFS.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single source of truth for dependency parsing, replacing three copies:
- dev-poll.sh get_deps() now calls parse-deps.py
- supervisor P3b/P3c import parse_deps() via importlib
Supports stdin, argument, and --json modes for different callers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The supervisor agent was confusingly named "factory" (same as the
project). Rename directory, script, log, lock, status, and escalation
files. Update all references across scripts and docs.
FACTORY_ROOT env var unchanged (refers to project root, not agent).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
do_merge() is defined at line 876, but recovery mode calls it at
line ~498. Bash requires functions to be defined before use.
Inlined the merge→rebase→re-approve→retry logic directly.
1. Recovery mode: if PR already has approval + green CI, try merge
immediately instead of entering the review wait loop forever.
2. do_merge: on 405/merge failure, rebase → force push → wait CI →
re-approve via review_bot → retry merge. Covers the stale-approval
dismissal problem end-to-end.
3. Codeberg mergeable field is unreliable — rebase on any merge failure.
Codeberg's mergeable field flickers between true/false — unreliable
for deciding whether to rebase. Just attempt rebase on any non-200/204.
Worst case it's a no-op. Also added git fetch before rebase.
- Remove write_state_entry/append_state_log from dev-agent (#10)
- Add planner-agent.sh: rebuilds STATE.md from git history + closed
issues, then gap-analyses against VISION.md to create backlog
issues (#6, #7)
- Add planner-poll.sh: cron wrapper with lock + memory guard
STATE.md is now solely owned by the planner — one compact snapshot
rebuilt each run, not an ever-growing append log.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When merge returns non-200, check mergeable flag. If false,
rebase the PR branch onto master via worktree. If rebase fails,
spawn dev-agent to resolve. Prevents infinite 405 retry loops.
Extracted try_merge_or_rebase() helper used at all 3 merge points.
Add matrix_send() to lib/env.sh and matrix_listener.sh daemon for
real-time notifications, threaded escalations, and human-in-the-loop
replies. All agents now notify via Matrix instead of openclaw.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PRs #684 and #710 had no issue number in branch name or title.
Now also checks PR body for 'Closes #NNN'. If still no issue found,
logs a skip (dev-agent requires an issue number to work).
PRs with custom branch names (fix/fitness-factory-address,
chore/seed-consolidation) were invisible to priority 1.5.
Now also extracts issue number from PR title (#NNN) as fallback.
Function was defined at line 867 but called at line 550. Bash requires
functions to be defined before invocation. Moved to top with other
helpers. Also removed duplicate definition.
1. PRIORITY 1.5 in dev-poll: scan ALL open PRs for REQUEST_CHANGES or CI
failure before picking new backlog issues. Stuck PRs get fixed first
to avoid complex rebases piling up.
2. STATE.md written in worktree before claude starts (included in first
commit, not a separate push that dismisses stale approvals).
3. Removed HTTP 405 from merge success check in dev-poll.sh (was fixed
in dev-agent.sh but not here — 2 occurrences).
When PR has merge conflicts (mergeable=false), attempt git rebase
before merge. If rebase fails, abort and escalate via notify.
Flow: approval → check mergeable → rebase if needed → wait CI → merge
Resolves the serial seed PR bottleneck where append-only files
(manifest.jsonl) create trivial conflicts that block the pipeline.
Root cause: Two bugs combined to silently close PRs without merging.
1. HTTP 405 ('not allowed to merge') was in the success condition
alongside 200/204. Codeberg returns 405 when branch protection
blocks the merge (e.g., stale approvals).
2. append_state_log pushed a new commit AFTER review_bot approved,
but BEFORE the merge attempt. With dismiss_stale_approvals=true,
the new commit automatically dismissed the approval → 405.
Impact: 6 PRs (#683, #688, #692, #695, #696, #699) were 'merged'
(logged as success, branch deleted, issue closed) but never actually
merged into master. All work was lost.
Fixes:
- Remove 405 from merge success check
- Move STATE.md append out of pre-merge path
The merged-PR search was over-engineered and caused false negatives
(couldn't match PR to issue when title/body didn't contain #NNN).
Issue closed = dep satisfied. Factory only closes after merging.
Codeberg uses shared issue/PR numbering. When a PR IS the dep issue
(e.g. PR #665 fixes issue #665), the title search misses it.
Fallback checks if pulls/{dep_num} is merged.
Moved from dark-factory to harb. Dev-agent appends one line to
STATE.md on the PR branch right before merge — goes through
review like any other change.
After each successful PR merge, dev-agent appends one line to
STATE.md: - [date] what now exists (#PR)
Lives in dark-factory repo (harb master is protected).
Planner will collapse this into a compact snapshot later.
When dev-agent exhausts CI fix budget, writes escalation marker.
Supervisor picks it up next poll and invokes claude -p to diagnose
(flaky test? rate limit? real bug?) and decide: fix, close PR, or
escalate to human.
- Moved ci-debug.sh from dev/ to lib/ (shared utility)
- README: fixed supervisor description (all alerts go to claude)
- README: replaced implementation details with actual design principles