forked from cleveragents/cleveragents-core
build(agents): prompt_async for pool supervisors, session resume, bot signatures, cleanup agent
Four changes in one commit across 27 agent files: 1. POOL SUPERVISOR PROMPT_ASYNC: All 4 pool supervisors (issue-implementor, ca-continuous-pr-reviewer, ca-uat-tester, ca-bug-hunter) now dispatch their internal workers via the OpenCode Server's prompt_async endpoint instead of the Task tool. This eliminates the wait_for_all bottleneck at the supervisor level — workers run independently, and a 10-second polling loop detects completions and immediately refills vacant slots. Added curl/sleep bash permissions where needed. Each supervisor keeps N workers running at all times with zero idle slots. 2. SESSION RESUME INSTEAD OF CLEANUP: The product-builder and all 4 pool supervisors now RESUME existing sessions from a previous interrupted run instead of aborting them. Phase C.0 queries the server for sessions titled "[CA-AUTO] supervisor:*" and adopts any that are still active into the monitoring loop. Pool supervisors similarly adopt existing "[CA-AUTO] worker-*" sessions. This enables "continue where you left off" — restarting the product-builder reconnects to running supervisors and workers rather than duplicating them. 3. DEDICATED CLEANUP AGENT: New ca-session-cleanup.md primary agent for explicit fresh-start cleanup. Run this BEFORE the product-builder when you want to abort all previous sessions and start completely fresh. It finds all "[CA-AUTO]" sessions, aborts them, and deletes them. This is the ONLY way to kill old sessions — the product-builder never does it automatically. 4. BOT SIGNATURES: All 26 agents that post content to Forgejo now include a mandatory "Bot Signature" section requiring every comment, issue body, PR description, and review to end with: --- **Automated by CleverAgents Bot** Supervisor: <category> | Agent: <agent-name> 24 agents have hardcoded categories. 2 shared agents (ca-new-issue-creator, ca-epic-planner) use a parameter-based category from their caller's prompt.
This commit is contained in:
@@ -358,6 +358,20 @@ Propose changes to model selection:
|
||||
|
||||
---
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Agent Evolver | Agent: ca-agent-evolver
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **NEVER apply changes directly.** All modifications go through PRs with
|
||||
|
||||
@@ -178,6 +178,20 @@ LOOP:
|
||||
bash("sleep 600", timeout=900000) # 10 min sleep, 15 min timeout
|
||||
```
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Architecture Guard | Agent: ca-architecture-guard
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Return Value
|
||||
|
||||
Report:
|
||||
|
||||
@@ -280,6 +280,20 @@ For each open legendary (`Type/Legendary`):
|
||||
|
||||
---
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Backlog Grooming | Agent: ca-backlog-groomer
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **No filesystem access.** You work entirely through the Forgejo API.
|
||||
|
||||
@@ -26,6 +26,8 @@ permission:
|
||||
"head *": allow
|
||||
"tail *": allow
|
||||
"git *": allow
|
||||
"curl *": allow
|
||||
"sleep *": allow
|
||||
task:
|
||||
"*": deny
|
||||
"ca-ref-reader": allow
|
||||
@@ -98,6 +100,20 @@ scanned_modules = set()
|
||||
findings_total = 0
|
||||
cycle = 0
|
||||
last_master_sha = query current master HEAD via Forgejo API
|
||||
SERVER = "http://localhost:4096"
|
||||
|
||||
# ── RESUME: Adopt existing hunt worker sessions from previous run ─
|
||||
EXISTING_WORKERS = bash("curl -s ${SERVER}/session | python3 -c \"
|
||||
import sys, json
|
||||
for s in json.loads(sys.stdin.read()):
|
||||
title = s.get('title','')
|
||||
if title.startswith('[CA-AUTO] worker-hunt:'):
|
||||
module = title.replace('[CA-AUTO] worker-hunt: ','')
|
||||
print(module + '=' + s['id'])
|
||||
\"", timeout=30000)
|
||||
|
||||
# Adopted workers will be picked up in the monitoring loop.
|
||||
# Mark their modules as in-progress so we don't dispatch duplicates.
|
||||
|
||||
LOOP:
|
||||
cycle += 1
|
||||
@@ -122,26 +138,54 @@ LOOP:
|
||||
bash("sleep 60", timeout=120000)
|
||||
continue # Loop back to check for new code
|
||||
|
||||
# ── Step 3: Dispatch N parallel workers ──────────────────────
|
||||
# ── Step 3: Dispatch workers via prompt_async ──────────────────
|
||||
# Fill all N slots. As each completes, immediately refill from unscanned.
|
||||
active = {} # module -> session_id
|
||||
batch = unscanned[:N]
|
||||
|
||||
workers = {}
|
||||
for module in batch:
|
||||
worker = invoke ca-bug-hunter with:
|
||||
- Repo owner/name
|
||||
- Instance ID: "bug-worker-<module-slug>-<cycle>"
|
||||
- Forgejo PAT, git identity, username
|
||||
- module_focus: module # Narrow scope → Worker Mode
|
||||
- max_workers: 1 # Force Worker Mode
|
||||
- spec_context: ref_summary
|
||||
workers[module] = worker
|
||||
SESSION_ID = bash("curl -s -X POST ${SERVER}/session \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{\"title\": \"[CA-AUTO] worker-hunt: <module>\"}' \
|
||||
| python3 -c \"import sys,json; print(json.loads(sys.stdin.read())['id'])\"",
|
||||
timeout=30000)
|
||||
bash("curl -s -X POST ${SERVER}/session/${SESSION_ID}/prompt_async \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{\"agent\": \"ca-bug-hunter\", \
|
||||
\"parts\": [{\"type\": \"text\", \"text\": \
|
||||
\"Worker mode. Module focus: <module>. max_workers: 1. \
|
||||
Repo: <owner>/<repo>. Forgejo PAT: <PAT>. \
|
||||
Git: <name> <email>. Username: <username>. \
|
||||
Acting on behalf of: Bug Hunting.\"}]}'",
|
||||
timeout=30000)
|
||||
active[module] = SESSION_ID
|
||||
|
||||
# ── Step 4: Collect results ──────────────────────────────────
|
||||
results = wait_for_all(workers)
|
||||
# ── Step 4: Monitor workers, collect results, refill slots ───
|
||||
remaining_unscanned = unscanned[N:] # modules not yet dispatched
|
||||
while active:
|
||||
bash("sleep 10", timeout=30000)
|
||||
STATUS = bash("curl -s ${SERVER}/session/status", timeout=30000)
|
||||
|
||||
for module, result in results.items():
|
||||
scanned_modules.add(module)
|
||||
findings_total += result.total_findings
|
||||
for module, session_id in list(active.items()):
|
||||
if session is completed or errored:
|
||||
# Collect result
|
||||
final_msg = bash("curl -s ${SERVER}/session/${session_id}/message",
|
||||
timeout=30000)
|
||||
result = parse_worker_result(final_msg)
|
||||
scanned_modules.add(module)
|
||||
findings_total += result.total_findings
|
||||
|
||||
# Clean up
|
||||
bash("curl -s -X DELETE ${SERVER}/session/${session_id}",
|
||||
timeout=15000)
|
||||
del active[module]
|
||||
|
||||
# Immediately refill slot from remaining unscanned modules
|
||||
if remaining_unscanned:
|
||||
next_module = remaining_unscanned.pop(0)
|
||||
# dispatch next_module (same prompt_async pattern as above)
|
||||
NEW_SID = create session + prompt_async for next_module
|
||||
active[next_module] = NEW_SID
|
||||
|
||||
# ── Step 5: Post progress ────────────────────────────────────
|
||||
if cycle % 2 == 0:
|
||||
@@ -369,6 +413,20 @@ Before filing any finding:
|
||||
|
||||
---
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Bug Hunting | Agent: ca-bug-hunter
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **NEVER work in /app.** Always use your isolated clone (Worker Mode) or
|
||||
|
||||
@@ -118,6 +118,22 @@ reviewed_prs = set() # PRs fully processed (merged or changes_requeste
|
||||
pending_merge = {} # PR number -> {attempts, last_status} — approved but not yet merged
|
||||
stale_count = 0 # Consecutive cycles with zero work found
|
||||
cycle = 0
|
||||
SERVER = "http://localhost:4096"
|
||||
|
||||
# ── RESUME: Adopt existing reviewer sessions from previous run ───
|
||||
# If there are worker sessions still running from a previous
|
||||
# interrupted run, adopt them into tracking instead of duplicating.
|
||||
EXISTING_WORKERS = bash("curl -s ${SERVER}/session | python3 -c \"
|
||||
import sys, json
|
||||
for s in json.loads(sys.stdin.read()):
|
||||
title = s.get('title','')
|
||||
if title.startswith('[CA-AUTO] worker-review:'):
|
||||
pr_num = title.replace('[CA-AUTO] worker-review: PR-','')
|
||||
print(pr_num + '=' + s['id'])
|
||||
\"", timeout=30000)
|
||||
|
||||
# Note: adopted workers will be picked up in the monitoring loop
|
||||
# automatically — they're tracked the same as freshly dispatched ones.
|
||||
|
||||
LOOP FOREVER:
|
||||
cycle += 1
|
||||
@@ -176,7 +192,7 @@ LOOP FOREVER:
|
||||
|
||||
stale_count = 0
|
||||
|
||||
# ── Step 3: Dispatch parallel reviewers ──────────────────────
|
||||
# ── Step 3: Dispatch parallel reviewers via prompt_async ─────
|
||||
# Take up to N work items
|
||||
batch = work_items[:N]
|
||||
|
||||
@@ -185,72 +201,87 @@ LOOP FOREVER:
|
||||
if item.type in ("review", "re_review"):
|
||||
post comment on PR:
|
||||
"Review claimed by reviewer pool instance <INSTANCE_ID>.
|
||||
Dispatching independent code review."
|
||||
Dispatching independent code review.
|
||||
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: PR Review | Agent: ca-continuous-pr-reviewer"
|
||||
|
||||
# Dispatch N parallel ca-pr-self-reviewer tasks
|
||||
active_reviews = {}
|
||||
# Dispatch N parallel ca-pr-self-reviewer sessions (fire-and-forget)
|
||||
active_reviews = {} # pr_number -> session_id
|
||||
for item in batch:
|
||||
if item.type in ("review", "re_review"):
|
||||
reviewer = invoke ca-pr-self-reviewer with:
|
||||
- repo: <owner>/<repo>
|
||||
- pr_number: item.pr.number
|
||||
- workdir: $CLONE_DIR
|
||||
- spec_context: ref_summary
|
||||
active_reviews[item.pr.number] = reviewer
|
||||
|
||||
elif item.type == "merge_retry":
|
||||
# For merge retries, dispatch the reviewer again to attempt merge
|
||||
reviewer = invoke ca-pr-self-reviewer with:
|
||||
- repo: <owner>/<repo>
|
||||
- pr_number: item.pr_number
|
||||
- workdir: $CLONE_DIR
|
||||
- spec_context: ref_summary
|
||||
- Note: "This PR was previously approved. Focus on merge."
|
||||
active_reviews[item.pr_number] = reviewer
|
||||
pr_num = item.pr.number if item.type != "merge_retry" else item.pr_number
|
||||
note = ""
|
||||
if item.type == "merge_retry":
|
||||
note = "This PR was previously approved. Focus on merge."
|
||||
|
||||
SESSION_ID = bash("curl -s -X POST ${SERVER}/session \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{\"title\": \"[CA-AUTO] worker-review: PR-<pr_num>\"}' \
|
||||
| python3 -c \"import sys,json; print(json.loads(sys.stdin.read())['id'])\"",
|
||||
timeout=30000)
|
||||
|
||||
bash("curl -s -X POST ${SERVER}/session/${SESSION_ID}/prompt_async \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{\"agent\": \"ca-pr-self-reviewer\", \
|
||||
\"parts\": [{\"type\": \"text\", \"text\": \
|
||||
\"Review PR #<pr_num>. Repo: <owner>/<repo>. \
|
||||
Spec context: <ref_summary compact>. \
|
||||
Forgejo PAT: <PAT>. Git: <name> <email>. \
|
||||
<note>\"}]}'", timeout=30000)
|
||||
|
||||
active_reviews[pr_num] = SESSION_ID
|
||||
|
||||
# ── Step 4: Collect results ──────────────────────────────────
|
||||
# Wait for all dispatched reviewers to complete
|
||||
results = wait_for_all(active_reviews)
|
||||
|
||||
for pr_number, result in results.items():
|
||||
if result.merge_status == "merged":
|
||||
reviewed_prs.add(pr_number)
|
||||
pending_merge.pop(pr_number, None)
|
||||
# PR is done — merged successfully
|
||||
|
||||
elif result.merge_status == "merge_scheduled":
|
||||
# Merge will happen when CI passes — track it
|
||||
pending_merge[pr_number] = {
|
||||
attempts: pending_merge.get(pr_number, {}).get(attempts, 0) + 1,
|
||||
last_status: "merge_scheduled"
|
||||
}
|
||||
|
||||
elif result.merge_status in ("ci_pending", "ci_failing", "merge_failed"):
|
||||
# Merge failed — track for retry next cycle
|
||||
pending_merge[pr_number] = {
|
||||
attempts: pending_merge.get(pr_number, {}).get(attempts, 0) + 1,
|
||||
last_status: result.merge_status
|
||||
}
|
||||
|
||||
elif result.merge_status == "conflict":
|
||||
# Merge conflict — post comment, mark as reviewed
|
||||
# The implementing agent needs to rebase
|
||||
post comment on PR #pr_number:
|
||||
"Merge conflict detected. The implementing agent needs to
|
||||
rebase this branch onto the latest master."
|
||||
reviewed_prs.add(pr_number)
|
||||
pending_merge.pop(pr_number, None)
|
||||
|
||||
elif result.decision == "changes_requested":
|
||||
# Changes requested — mark as reviewed for now
|
||||
# Will re-enter work queue when new commits are pushed
|
||||
reviewed_prs.add(pr_number)
|
||||
pending_merge.pop(pr_number, None)
|
||||
|
||||
elif result.merge_status == "awaiting_human":
|
||||
# `needs feedback` PR — skip permanently
|
||||
reviewed_prs.add(pr_number)
|
||||
pending_merge.pop(pr_number, None)
|
||||
# ── Step 4: Monitor workers, collect results ─────────────────
|
||||
# Poll every 10 seconds until all dispatched reviewers complete.
|
||||
# As each completes, process its result immediately.
|
||||
while active_reviews:
|
||||
bash("sleep 10", timeout=30000)
|
||||
STATUS = bash("curl -s ${SERVER}/session/status", timeout=30000)
|
||||
|
||||
for pr_number, session_id in list(active_reviews.items()):
|
||||
session_status = parse STATUS for session_id
|
||||
if session is completed or errored or not found:
|
||||
# Collect result from session's final message
|
||||
final_msg = bash("curl -s ${SERVER}/session/${session_id}/message",
|
||||
timeout=30000)
|
||||
result = parse_reviewer_result(final_msg)
|
||||
|
||||
# Clean up worker session
|
||||
bash("curl -s -X DELETE ${SERVER}/session/${session_id}",
|
||||
timeout=15000)
|
||||
del active_reviews[pr_number]
|
||||
|
||||
# Process result:
|
||||
if result.merge_status == "merged":
|
||||
reviewed_prs.add(pr_number)
|
||||
pending_merge.pop(pr_number, None)
|
||||
elif result.merge_status == "merge_scheduled":
|
||||
pending_merge[pr_number] = {
|
||||
attempts: pending_merge.get(pr_number, {}).get(attempts, 0) + 1,
|
||||
last_status: "merge_scheduled"
|
||||
}
|
||||
elif result.merge_status in ("ci_pending", "ci_failing", "merge_failed"):
|
||||
pending_merge[pr_number] = {
|
||||
attempts: pending_merge.get(pr_number, {}).get(attempts, 0) + 1,
|
||||
last_status: result.merge_status
|
||||
}
|
||||
elif result.merge_status == "conflict":
|
||||
post comment on PR #pr_number:
|
||||
"Merge conflict detected. The implementing agent
|
||||
needs to rebase this branch onto latest master.
|
||||
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: PR Review | Agent: ca-continuous-pr-reviewer"
|
||||
reviewed_prs.add(pr_number)
|
||||
pending_merge.pop(pr_number, None)
|
||||
elif result.decision == "changes_requested":
|
||||
reviewed_prs.add(pr_number)
|
||||
pending_merge.pop(pr_number, None)
|
||||
elif result.merge_status == "awaiting_human":
|
||||
reviewed_prs.add(pr_number)
|
||||
pending_merge.pop(pr_number, None)
|
||||
|
||||
# ── Step 5: Check for scheduled merges that completed ────────
|
||||
# PRs with merge_when_checks_succeed may have merged since last cycle
|
||||
@@ -326,6 +357,20 @@ they choose, but the system never gives up autonomously.
|
||||
|
||||
---
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: PR Review | Agent: ca-continuous-pr-reviewer
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **NEVER work in /app.** Always use your isolated clone.
|
||||
|
||||
@@ -163,6 +163,20 @@ For complex modules, produce per-module docs in `docs/modules/`.
|
||||
- Explain purpose, key classes/functions, usage patterns, and gotchas
|
||||
- Include code examples where they aid understanding
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Documentation | Agent: ca-docs-writer
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
1. **EXTEND, never overwrite.** Read existing docs first. Merge new content into existing structure.
|
||||
|
||||
@@ -166,6 +166,24 @@ matters:
|
||||
Document dependencies in each issue's Dependencies section. The first
|
||||
issues in any chain should be the ones with zero blockers.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: <CATEGORY> | Agent: ca-epic-planner
|
||||
```
|
||||
|
||||
**Category**: Use the supervisor category provided by your caller in the
|
||||
prompt (e.g., "Acting on behalf of: UAT Testing"). If no category was
|
||||
provided, use "Unknown".
|
||||
**Agent**: ca-epic-planner
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
|
||||
## Return Value
|
||||
|
||||
Report back with:
|
||||
|
||||
@@ -487,6 +487,20 @@ Every 15 cycles (~30 minutes), check for conversations that need attention:
|
||||
|
||||
---
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Human Liaison | Agent: ca-human-liaison
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **No filesystem access.** You work entirely through the Forgejo API and
|
||||
|
||||
@@ -89,6 +89,20 @@ sections (skip only if truly not applicable):
|
||||
### Risk Mitigations
|
||||
- Potential risks identified and how they were addressed.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Implementation | Agent: ca-issue-note-writer
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important
|
||||
|
||||
- Be **exhaustive**. This is non-optional. Every decision, discovery, and
|
||||
|
||||
@@ -71,6 +71,20 @@ Execute these steps:
|
||||
- Which labels were added
|
||||
- Whether any precondition checks failed
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Implementation | Agent: ca-issue-state-updater
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important
|
||||
|
||||
- Only modify state labels (`State/*`) and the `Blocked` label.
|
||||
|
||||
@@ -351,6 +351,20 @@ After the PR is created:
|
||||
|
||||
---
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Implementation | Agent: ca-issue-worker
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Forgejo Comment Protocol
|
||||
|
||||
Post comments on the Forgejo issue (via `ca-issue-note-writer` or direct
|
||||
|
||||
@@ -225,6 +225,20 @@ session state issue) using the format below.
|
||||
specification requirements are unmet. The milestone needs additional work
|
||||
before it can be considered complete.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Implementation | Agent: ca-milestone-reviewer
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **You are read-only.** You review code and create issues. You do NOT fix
|
||||
|
||||
@@ -99,6 +99,24 @@ Follow the format specified in CONTRIBUTING.md "Creating Issues":
|
||||
- If the new issue blocks the current issue, document this dependency.
|
||||
- If it does not block, note that it is independent.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: <CATEGORY> | Agent: ca-new-issue-creator
|
||||
```
|
||||
|
||||
**Category**: Use the supervisor category provided by your caller in the
|
||||
prompt (e.g., "Acting on behalf of: UAT Testing"). If no category was
|
||||
provided, use "Unknown".
|
||||
**Agent**: ca-new-issue-creator
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
|
||||
## Return Value
|
||||
|
||||
Report back with:
|
||||
|
||||
@@ -97,6 +97,20 @@ forgejo_update_pull_request(owner, repo, index, milestone="3", body=pr.body)
|
||||
|
||||
This applies to ALL PR modifications after initial creation.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Implementation | Agent: ca-pr-api-creator
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important
|
||||
|
||||
- The PR MUST reference the issue with a closing keyword in the body
|
||||
|
||||
@@ -159,6 +159,20 @@ forgejo_update_pull_request(owner, repo, index, title="new title", body=pr.body)
|
||||
This applies to ALL PR modifications: title changes, milestone updates,
|
||||
assignee changes, label additions — EVERY update call must include `body`.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: PR Review | Agent: ca-pr-checker
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Critical Rules
|
||||
|
||||
- **Amend** the existing commit when fixing. Do NOT create new commits.
|
||||
|
||||
@@ -246,6 +246,20 @@ title, assignee, milestone, or any other field), you MUST:
|
||||
Failing to do this will replace the PR description with an empty string.
|
||||
This applies to ALL `forgejo_update_pull_request` calls without exception.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: PR Review | Agent: ca-pr-self-reviewer
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- You are a **DIFFERENT PERSPECTIVE** than the implementer. Do not rubber-stamp.
|
||||
|
||||
@@ -134,6 +134,20 @@ Perform each check in order. Record PASS or FAIL with details for every item.
|
||||
- **COMPLETE**: All 10 checks pass. The product is ready.
|
||||
- **INCOMPLETE**: One or more checks fail. List every gap with actionable remediation.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Product Verification | Agent: ca-product-verifier
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Return Value
|
||||
|
||||
Output the following structured report:
|
||||
|
||||
@@ -196,6 +196,20 @@ Create the following directories (with `.gitkeep` files to ensure they are track
|
||||
- Push to `master` (this is initial setup, before branch protection is applied)
|
||||
- Apply branch protection LAST so the setup commits can be pushed directly
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Project Bootstrap | Agent: ca-project-bootstrapper
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Return Value
|
||||
|
||||
Provide a structured report:
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
description: >
|
||||
One-shot cleanup agent that finds and aborts ALL stale automated sessions
|
||||
from previous interrupted runs. Run this BEFORE starting a fresh
|
||||
product-builder session to prevent duplicate supervisors and workers.
|
||||
Do NOT run this if you want to resume an existing session — the
|
||||
product-builder will reconnect to existing sessions automatically.
|
||||
mode: primary
|
||||
temperature: 0.0
|
||||
color: error
|
||||
permission:
|
||||
edit: deny
|
||||
bash:
|
||||
"*": deny
|
||||
"curl *": allow
|
||||
"sleep *": allow
|
||||
"echo *": allow
|
||||
task:
|
||||
"*": deny
|
||||
---
|
||||
|
||||
# CleverAgents Session Cleanup
|
||||
|
||||
You are a one-shot cleanup agent. Your job is to find and abort ALL
|
||||
automated sessions from previous product-builder runs that may still be
|
||||
running on the OpenCode server.
|
||||
|
||||
**When to use this agent:**
|
||||
- Before starting a FRESH product-builder session (not resuming)
|
||||
- After a crash, interrupt, or unclean shutdown
|
||||
- When you see duplicate supervisors or workers competing for work
|
||||
|
||||
**When NOT to use this agent:**
|
||||
- When resuming an existing session (the product-builder reconnects
|
||||
automatically)
|
||||
- When the system is running normally
|
||||
|
||||
## Process
|
||||
|
||||
```
|
||||
SERVER = "http://localhost:4096"
|
||||
|
||||
# Step 1: Query all sessions from the server
|
||||
ALL_SESSIONS = bash("curl -s ${SERVER}/session", timeout=30000)
|
||||
|
||||
# Step 2: Find all automated sessions (supervisors and workers)
|
||||
# Automated sessions have titles starting with "[CA-AUTO]"
|
||||
AUTOMATED = bash("echo '${ALL_SESSIONS}' | python3 -c \"
|
||||
import sys, json
|
||||
sessions = json.loads(sys.stdin.read())
|
||||
for s in sessions:
|
||||
title = s.get('title', '')
|
||||
if title.startswith('[CA-AUTO]'):
|
||||
print(s['id'] + ' | ' + title)
|
||||
\"", timeout=30000)
|
||||
|
||||
# Step 3: Report what was found
|
||||
if AUTOMATED is empty:
|
||||
Output: "No stale automated sessions found. The server is clean."
|
||||
EXIT
|
||||
|
||||
Output: "Found <N> automated sessions to clean up:"
|
||||
for each line in AUTOMATED:
|
||||
Output: " - <session_id> | <title>"
|
||||
|
||||
# Step 4: Abort and delete each session
|
||||
aborted = 0
|
||||
for session_id in AUTOMATED (extract ID from each line):
|
||||
bash("curl -s -X POST ${SERVER}/session/${session_id}/abort", timeout=15000)
|
||||
bash("curl -s -X DELETE ${SERVER}/session/${session_id}", timeout=15000)
|
||||
aborted += 1
|
||||
|
||||
# Step 5: Clean up tracking files
|
||||
bash("rm -f /tmp/ca-supervisor-sessions.env", timeout=5000)
|
||||
bash("rm -f /tmp/ca-worker-impl-sessions.env", timeout=5000)
|
||||
|
||||
# Step 6: Report results
|
||||
Output: "Cleanup complete. Aborted and deleted <aborted> automated sessions."
|
||||
Output: "You can now start a fresh product-builder session."
|
||||
```
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **This is a one-shot agent.** Run it once, it cleans up, it exits.
|
||||
- **It kills EVERYTHING with [CA-AUTO] in the title.** Both supervisors
|
||||
and workers. There is no selective cleanup.
|
||||
- **Do NOT run this while the product-builder is actively running.** It
|
||||
will kill all its supervisors and workers.
|
||||
- **The product-builder does NOT need this to start.** If resuming, the
|
||||
product-builder reconnects to existing sessions automatically. This
|
||||
agent is only for starting completely fresh.
|
||||
@@ -65,6 +65,20 @@ If restarting: [specific instructions for what to do next]
|
||||
|
||||
Read the session state issue, find the latest comment, parse and return the state.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Session Management | Agent: ca-session-persister
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- ALWAYS create a NEW comment (never edit old ones) — preserves full audit history
|
||||
|
||||
@@ -202,6 +202,20 @@ update metadata via `forgejo_update_pull_request`), you MUST:
|
||||
Failing to do this will replace the PR description with an empty string,
|
||||
wiping the detailed rationale and change descriptions you wrote.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Spec Evolution | Agent: ca-spec-updater
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- The spec is the **SOURCE OF TRUTH**. Only update it when the implementation genuinely discovered a better approach.
|
||||
|
||||
@@ -55,6 +55,20 @@ You will be given:
|
||||
- Whether any provided subtask descriptions did not match (fuzzy match
|
||||
is acceptable if the meaning is clearly the same)
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Implementation | Agent: ca-subtask-checker
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important
|
||||
|
||||
- Only modify checkbox states. Do NOT change any other part of the issue body.
|
||||
|
||||
@@ -342,6 +342,20 @@ KEY_DESIGN_DECISIONS:
|
||||
trade-offs, and reasoning that the parent issue worker should know about>
|
||||
```
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Implementation | Agent: ca-subtask-loop
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **NEVER give up on a subtask.** Loop forever until it passes. The
|
||||
|
||||
@@ -409,6 +409,20 @@ Story points per developer per milestone.
|
||||
- PRs: X open (was Y)
|
||||
```
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Timeline | Agent: ca-timeline-updater
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **NEVER delete historical data.** Schedule adherence entries, completed
|
||||
|
||||
@@ -90,6 +90,20 @@ feature_areas = extract_all_feature_areas(ref_summary)
|
||||
tested_areas = set()
|
||||
bugs_found_total = 0
|
||||
cycle = 0
|
||||
SERVER = "http://localhost:4096"
|
||||
|
||||
# ── RESUME: Adopt existing UAT worker sessions from previous run ─
|
||||
EXISTING_WORKERS = bash("curl -s ${SERVER}/session | python3 -c \"
|
||||
import sys, json
|
||||
for s in json.loads(sys.stdin.read()):
|
||||
title = s.get('title','')
|
||||
if title.startswith('[CA-AUTO] worker-uat:'):
|
||||
area = title.replace('[CA-AUTO] worker-uat: ','')
|
||||
print(area + '=' + s['id'])
|
||||
\"", timeout=30000)
|
||||
|
||||
# Adopted workers will be picked up in the monitoring loop.
|
||||
# Mark their areas as in-progress so we don't dispatch duplicates.
|
||||
|
||||
LOOP:
|
||||
cycle += 1
|
||||
@@ -112,26 +126,54 @@ LOOP:
|
||||
bash("sleep 60", timeout=120000)
|
||||
continue # Loop back to check for new code
|
||||
|
||||
# ── Step 2: Dispatch N parallel workers ──────────────────────
|
||||
# ── Step 2: Dispatch workers via prompt_async ──────────────────
|
||||
# Fill all N slots. As each completes, immediately refill from untested.
|
||||
active = {} # area -> session_id
|
||||
batch = untested[:N]
|
||||
|
||||
workers = {}
|
||||
for area in batch:
|
||||
worker = invoke ca-uat-tester with:
|
||||
- Repo owner/name
|
||||
- Instance ID: "uat-worker-<area-slug>-<cycle>"
|
||||
- Forgejo PAT, git identity, username
|
||||
- feature_area: area # Narrow scope → Worker Mode
|
||||
- max_workers: 1 # Force Worker Mode
|
||||
- spec_context: ref_summary
|
||||
workers[area] = worker
|
||||
SESSION_ID = bash("curl -s -X POST ${SERVER}/session \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{\"title\": \"[CA-AUTO] worker-uat: <area>\"}' \
|
||||
| python3 -c \"import sys,json; print(json.loads(sys.stdin.read())['id'])\"",
|
||||
timeout=30000)
|
||||
bash("curl -s -X POST ${SERVER}/session/${SESSION_ID}/prompt_async \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{\"agent\": \"ca-uat-tester\", \
|
||||
\"parts\": [{\"type\": \"text\", \"text\": \
|
||||
\"Worker mode. Feature area: <area>. max_workers: 1. \
|
||||
Repo: <owner>/<repo>. Forgejo PAT: <PAT>. \
|
||||
Git: <name> <email>. Username: <username>. \
|
||||
Acting on behalf of: UAT Testing.\"}]}'",
|
||||
timeout=30000)
|
||||
active[area] = SESSION_ID
|
||||
|
||||
# ── Step 3: Collect results ──────────────────────────────────
|
||||
results = wait_for_all(workers)
|
||||
# ── Step 3: Monitor workers, collect results, refill slots ───
|
||||
remaining_untested = untested[N:] # areas not yet dispatched
|
||||
while active:
|
||||
bash("sleep 10", timeout=30000)
|
||||
STATUS = bash("curl -s ${SERVER}/session/status", timeout=30000)
|
||||
|
||||
for area, result in results.items():
|
||||
tested_areas.add(area)
|
||||
bugs_found_total += result.bugs_filed
|
||||
for area, session_id in list(active.items()):
|
||||
if session is completed or errored:
|
||||
# Collect result
|
||||
final_msg = bash("curl -s ${SERVER}/session/${session_id}/message",
|
||||
timeout=30000)
|
||||
result = parse_worker_result(final_msg)
|
||||
tested_areas.add(area)
|
||||
bugs_found_total += result.bugs_filed
|
||||
|
||||
# Clean up
|
||||
bash("curl -s -X DELETE ${SERVER}/session/${session_id}",
|
||||
timeout=15000)
|
||||
del active[area]
|
||||
|
||||
# Immediately refill slot from remaining untested areas
|
||||
if remaining_untested:
|
||||
next_area = remaining_untested.pop(0)
|
||||
# dispatch next_area (same prompt_async pattern as above)
|
||||
NEW_SID = create session + prompt_async for next_area
|
||||
active[next_area] = NEW_SID
|
||||
|
||||
# ── Step 4: Post progress ────────────────────────────────────
|
||||
if cycle % 2 == 0:
|
||||
@@ -339,6 +381,20 @@ Before filing any bug:
|
||||
|
||||
---
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: UAT Testing | Agent: ca-uat-tester
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **NEVER work in /app.** Always use your isolated clone (Worker Mode) or
|
||||
|
||||
@@ -17,6 +17,8 @@ permission:
|
||||
bash:
|
||||
"*": deny
|
||||
"echo $*": allow
|
||||
"curl *": allow
|
||||
"sleep *": allow
|
||||
task:
|
||||
"*": deny
|
||||
"ca-ref-reader": allow
|
||||
@@ -176,32 +178,68 @@ maximum throughput.
|
||||
max_workers = CA_MAX_PARALLEL_WORKERS (from env, or ask user if unset)
|
||||
queue = prioritized list of unblocked issues (ready, or blocked only
|
||||
by issues assigned to you in this batch)
|
||||
active = {} # issue_number -> running worker
|
||||
active = {} # issue_number -> session_id (prompt_async session)
|
||||
completed = [] # list of {issue_number, branch, pr_number, pr_url, ...}
|
||||
failed = {} # issue_number -> consecutive_failure_count
|
||||
pre_cloned = {} # issue_number -> clone_path (speculatively prepared)
|
||||
ref_summary = result from ca-ref-reader
|
||||
wave = 1
|
||||
last_timeline_update = now()
|
||||
bg_ref_reader = None # background ref-reader task (if running)
|
||||
bg_timeline = None # background timeline-updater task (if running)
|
||||
SERVER = "http://localhost:4096"
|
||||
|
||||
idle_polls = 0
|
||||
# ── RESUME: Adopt existing worker sessions from previous run ─────
|
||||
# If this supervisor is picking up from a previous interrupted run,
|
||||
# there may be worker sessions still running. Adopt them into the
|
||||
# active tracking instead of launching duplicates.
|
||||
# To start fresh, run ca-session-cleanup BEFORE the product-builder.
|
||||
|
||||
while queue is not empty or active is not empty or idle_polls < 60:
|
||||
EXISTING_WORKERS = bash("curl -s ${SERVER}/session | python3 -c \"
|
||||
import sys, json
|
||||
for s in json.loads(sys.stdin.read()):
|
||||
title = s.get('title','')
|
||||
if title.startswith('[CA-AUTO] worker-impl:'):
|
||||
# Extract issue number from title
|
||||
issue_num = title.replace('[CA-AUTO] worker-impl: issue-','')
|
||||
print(issue_num + '=' + s['id'])
|
||||
\"", timeout=30000)
|
||||
|
||||
# ── Collect background task results (non-blocking) ──
|
||||
if bg_ref_reader is not None and bg_ref_reader.done:
|
||||
ref_summary = bg_ref_reader.result
|
||||
bg_ref_reader = None
|
||||
if bg_timeline is not None and bg_timeline.done:
|
||||
last_timeline_update = now()
|
||||
bg_timeline = None
|
||||
STATUS = bash("curl -s ${SERVER}/session/status", timeout=30000)
|
||||
for line in EXISTING_WORKERS:
|
||||
issue_number, session_id = line.split("=")
|
||||
if session_id is active in STATUS:
|
||||
active[int(issue_number)] = session_id # Adopt into active tracking
|
||||
# Remove from queue if present (already being worked on)
|
||||
queue = [i for i in queue if i.number != int(issue_number)]
|
||||
|
||||
# ── AGGRESSIVE slot-filling: fill ALL empty slots at once ──
|
||||
# Do not stop after one dispatch — fill every available slot.
|
||||
# ── Helper: launch one worker via prompt_async ───────────────────
|
||||
function dispatch_worker(issue, base_branch, ref_summary):
|
||||
prompt = "You are an issue worker for Implementation.
|
||||
Ref summary: <ref_summary (compact)>
|
||||
Issue: #<issue.number> — <issue.title>
|
||||
Branch: <issue.branch>
|
||||
Milestone: <issue.milestone>
|
||||
Labels: <issue.labels>
|
||||
Forgejo PAT: <PAT>. Git: <name> <email>. Username: <username>.
|
||||
Base branch: <base_branch or 'master'>"
|
||||
|
||||
SESSION_ID = bash("curl -s -X POST ${SERVER}/session \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{\"title\": \"[CA-AUTO] worker-impl: issue-<issue.number>\"}' \
|
||||
| python3 -c \"import sys,json; print(json.loads(sys.stdin.read())['id'])\"",
|
||||
timeout=30000)
|
||||
|
||||
bash("curl -s -X POST ${SERVER}/session/${SESSION_ID}/prompt_async \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{\"agent\": \"ca-issue-worker\", \
|
||||
\"parts\": [{\"type\": \"text\", \"text\": \"<prompt>\"}]}'",
|
||||
timeout=30000)
|
||||
|
||||
return SESSION_ID
|
||||
|
||||
# ── Main dispatch + monitoring loop ──────────────────────────────
|
||||
|
||||
LOOP FOREVER:
|
||||
|
||||
# ── AGGRESSIVE slot-filling: fill ALL empty slots at once ────
|
||||
slots_available = max_workers - len(active)
|
||||
dispatched_this_round = 0
|
||||
while slots_available > 0 and queue is not empty:
|
||||
issue = queue.pop(0) # highest priority first
|
||||
|
||||
@@ -210,114 +248,75 @@ while queue is not empty or active is not empty or idle_polls < 60:
|
||||
if issue depends on a completed issue from this session:
|
||||
base_branch = completed_issue.branch_name
|
||||
|
||||
# Check if this issue was speculatively pre-cloned
|
||||
pre_clone_path = pre_cloned.pop(issue.number, None)
|
||||
|
||||
# Dispatch ca-issue-worker
|
||||
worker = invoke ca-issue-worker with:
|
||||
- ref_summary
|
||||
- issue details (number, title, branch, milestone, labels)
|
||||
- Forgejo PAT, git identity, username
|
||||
- base_branch (if dependent on a completed issue)
|
||||
- pre_clone_path (if available, worker skips Phase 1 clone)
|
||||
active[issue.number] = worker
|
||||
# Dispatch via prompt_async (fire-and-forget)
|
||||
session_id = dispatch_worker(issue, base_branch, ref_summary)
|
||||
active[issue.number] = session_id
|
||||
slots_available -= 1
|
||||
dispatched_this_round += 1
|
||||
|
||||
# ── SPECULATIVE PRE-CLONING for blocked issues ──────────────────
|
||||
# For issues whose blockers are >50% complete (more than half their
|
||||
# subtasks checked off), speculatively clone the repo and set up the
|
||||
# branch. This eliminates clone+branch setup latency when the blocker
|
||||
# finishes.
|
||||
# ── Monitor active workers: poll every 10 seconds ────────────
|
||||
# Check which workers have completed. For each completed worker,
|
||||
# collect its result and free the slot. Then loop back to fill
|
||||
# empty slots immediately.
|
||||
#
|
||||
# This runs in the BACKGROUND and does NOT count against max_workers.
|
||||
for blocked_issue in issues_blocked_by_active_workers:
|
||||
if blocked_issue.number not in pre_cloned:
|
||||
blocker = get_blocker(blocked_issue)
|
||||
if blocker_progress(blocker) > 50%: # >50% subtasks done
|
||||
# Pre-clone in background (lightweight bash, not a worker slot)
|
||||
pre_clone_path = "/tmp/cleveragents-" + blocked_issue.branch
|
||||
run_in_background:
|
||||
git clone <repo> <pre_clone_path>
|
||||
git checkout -b <blocked_issue.branch>
|
||||
configure git identity
|
||||
pre_cloned[blocked_issue.number] = pre_clone_path
|
||||
# MUST use Bash tool for sleep:
|
||||
bash("sleep 10", timeout=30000)
|
||||
|
||||
# ── Wait for ANY active worker to complete ──
|
||||
completed_worker = wait_for_any(active)
|
||||
issue = completed_worker.issue
|
||||
del active[issue.number]
|
||||
STATUS = bash("curl -s ${SERVER}/session/status", timeout=30000)
|
||||
|
||||
if completed_worker.success:
|
||||
completed.append({
|
||||
issue_number, branch, pr_number, pr_url, pass/fail,
|
||||
attempt_counts, model_tiers_used, new_issues_created
|
||||
})
|
||||
for issue_number, session_id in active.items():
|
||||
session_status = parse STATUS for session_id
|
||||
|
||||
# Check if any blocked issues are now unblocked
|
||||
newly_unblocked = find issues whose blockers are all in completed
|
||||
queue.extend(newly_unblocked) # insert respecting priority order
|
||||
re-sort queue by priority
|
||||
if session is completed or errored or not found:
|
||||
# Collect result: query session for final message
|
||||
final_msg = bash("curl -s ${SERVER}/session/${session_id}/message \
|
||||
| python3 -c \"import sys,json; msgs=json.loads(sys.stdin.read()); \
|
||||
print(msgs[-1] if msgs else 'ERROR')\"", timeout=30000)
|
||||
|
||||
wave += 1
|
||||
# Parse worker result from final message
|
||||
if worker reported success:
|
||||
completed.append({
|
||||
issue_number, branch, pr_number, pr_url,
|
||||
attempt_counts, model_tiers_used, new_issues_created
|
||||
})
|
||||
|
||||
# ── BACKGROUND ref-reader refresh every 3 waves ──
|
||||
# Run in background — do NOT block the dispatch loop.
|
||||
if wave > 1 and wave % 3 == 0 and bg_ref_reader is None:
|
||||
bg_ref_reader = invoke ca-ref-reader IN BACKGROUND (non-blocking)
|
||||
# Unblock dependent issues
|
||||
newly_unblocked = issues whose blockers are all in completed
|
||||
queue.extend(newly_unblocked)
|
||||
re-sort queue by priority
|
||||
wave += 1
|
||||
|
||||
# ── BACKGROUND timeline update every 3 waves ──
|
||||
# Run in background — do NOT block the dispatch loop.
|
||||
if wave > 1 and bg_timeline is None and \
|
||||
(wave % 3 == 0 or hours_since(last_timeline_update) > 24):
|
||||
bg_timeline = invoke ca-timeline-updater IN BACKGROUND with:
|
||||
- Working directory: /app
|
||||
- Repo: cleveragents/cleveragents-core
|
||||
- Session context: compact ledger of completed issues so far,
|
||||
current active workers, failed issues with retry counts, open PR count
|
||||
- Current day number
|
||||
else: # Worker failed — always re-queue, never give up
|
||||
consecutive = failed.get(issue_number, 0) + 1
|
||||
failed[issue_number] = consecutive
|
||||
|
||||
else: # Worker failed — always re-queue, never give up
|
||||
consecutive = failed.get(issue.number, 0) + 1
|
||||
failed[issue.number] = consecutive
|
||||
if consecutive % 3 == 0:
|
||||
post comment on Forgejo issue #issue_number:
|
||||
"Implementation attempt <consecutive> failed.
|
||||
Retrying with a fresh approach.
|
||||
|
||||
# Post diagnostic comment every 3 consecutive failures
|
||||
if consecutive % 3 == 0:
|
||||
post comment on Forgejo issue #issue.number:
|
||||
"Implementation attempt <consecutive> failed.
|
||||
Error: <completed_worker.error summary>
|
||||
Retrying with a fresh approach (clearing prior attempt context
|
||||
to avoid repeating the same mistakes)."
|
||||
# Reset approach: next attempt starts without prior attempt log
|
||||
# so the implementer takes a fresh approach instead of
|
||||
# compounding the same failing strategy.
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Implementation | Agent: issue-implementor"
|
||||
|
||||
queue.append(issue) # always re-queue for retry
|
||||
queue.append(issue) # always re-queue
|
||||
|
||||
# ── Idle polling: discover new work from Forgejo ──────────────
|
||||
# When queue is empty and no workers are active, poll Forgejo for
|
||||
# new issues (UAT bugs, human-created issues, etc.) before exiting.
|
||||
# Clean up the completed session
|
||||
bash("curl -s -X DELETE ${SERVER}/session/${session_id}", timeout=15000)
|
||||
del active[issue_number]
|
||||
|
||||
# ── Idle polling: discover new work from Forgejo ─────────────
|
||||
if queue is empty and active is empty:
|
||||
# Sleep 60 seconds then poll for new work. NEVER exit/break.
|
||||
# MUST use Bash tool: bash("sleep 60", timeout=120000)
|
||||
bash("sleep 60", timeout=120000)
|
||||
new_issues = query Forgejo for new open issues assigned to me
|
||||
with State/Verified or State/In Progress labels
|
||||
if new_issues:
|
||||
queue.extend(new_issues)
|
||||
re-sort queue by priority
|
||||
idle_polls = 0 # Reset idle counter — new work found
|
||||
else:
|
||||
idle_polls += 1
|
||||
# DO NOT break or exit. Sleep and poll again.
|
||||
continue # Loop back to check again
|
||||
# DO NOT break or exit. Loop back to slot-filling.
|
||||
|
||||
idle_polls = 0 # Reset whenever there IS work
|
||||
|
||||
# ── IMMEDIATELY loop back to slot-filling ──
|
||||
# Do NOT wait or pause between iterations. As soon as one worker
|
||||
# completes, fill all empty slots and continue. Maximum throughput
|
||||
# means zero idle worker slots.
|
||||
# ── IMMEDIATELY loop back to slot-filling ────────────────────
|
||||
# Maximum throughput: zero idle worker slots.
|
||||
```
|
||||
|
||||
### Key dispatch rules
|
||||
@@ -362,6 +361,20 @@ Discard all other worker output. This compact ledger is what you carry forward
|
||||
and eventually pass to `ca-final-reporter`. Retaining full worker output will
|
||||
exhaust context in sessions with many issues — avoid this at all costs.
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Implementation | Agent: issue-implementor
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Coordination Rules
|
||||
|
||||
- **One worker per branch.** Never have two workers on the same branch.
|
||||
|
||||
@@ -359,54 +359,48 @@ milestones = list of all milestones to complete (ordered)
|
||||
ref_summary = result from ca-ref-reader
|
||||
SERVER = "http://localhost:4096"
|
||||
|
||||
# ── PHASE C.0: Clean Up Stale Supervisor Sessions ───────────────
|
||||
# If the product-builder was previously interrupted (Ctrl+C, crash,
|
||||
# session timeout, etc.), old supervisor sessions launched via
|
||||
# prompt_async may still be running on the server. They are
|
||||
# independent sessions that survive the product-builder's death.
|
||||
# ── PHASE C.0: Resume Existing Supervisor Sessions ──────────────
|
||||
# Check if there are already-running supervisor sessions from a
|
||||
# previous product-builder invocation. If found, ADOPT them into
|
||||
# the monitoring loop instead of launching duplicates.
|
||||
#
|
||||
# We MUST find and abort them before launching fresh supervisors,
|
||||
# otherwise there will be duplicate supervisors competing for the
|
||||
# same work — duplicate PR reviews, conflicting git pushes, etc.
|
||||
# This enables "continue where you left off" — if the user restarts
|
||||
# the product-builder, it reconnects to the existing supervisors
|
||||
# rather than killing them and starting fresh.
|
||||
#
|
||||
# Identification: All supervisor sessions have titles starting with
|
||||
# "[CA-AUTO] supervisor:". This prefix is unique to product-builder-
|
||||
# managed supervisors and will not match user-created sessions.
|
||||
# To start completely fresh (kill all old sessions), run the
|
||||
# ca-session-cleanup agent BEFORE starting the product-builder.
|
||||
|
||||
# Step 1: Query all sessions from the server
|
||||
ALL_SESSIONS=$(bash("curl -s ${SERVER}/session", timeout=30000))
|
||||
ALL_SESSIONS = bash("curl -s ${SERVER}/session", timeout=30000)
|
||||
|
||||
# Step 2: Extract sessions whose title starts with "[CA-AUTO] supervisor:"
|
||||
# Use python3 to parse JSON and filter:
|
||||
STALE_IDS=$(bash("echo '${ALL_SESSIONS}' | python3 -c \"
|
||||
# Step 2: Find existing supervisor sessions
|
||||
EXISTING = bash("echo '${ALL_SESSIONS}' | python3 -c \"
|
||||
import sys, json
|
||||
sessions = json.loads(sys.stdin.read())
|
||||
for s in sessions:
|
||||
if s.get('title', '').startswith('[CA-AUTO] supervisor:'):
|
||||
print(s['id'])
|
||||
\"", timeout=30000))
|
||||
title = s.get('title', '')
|
||||
if title.startswith('[CA-AUTO] supervisor:'):
|
||||
# Extract the display name from title
|
||||
name = title.replace('[CA-AUTO] supervisor: ', '')
|
||||
print(name + '=' + s['id'])
|
||||
\"", timeout=30000)
|
||||
|
||||
# Step 3: Abort and delete each stale session
|
||||
stale_count = 0
|
||||
for session_id in STALE_IDS (one per line):
|
||||
# Abort if still running
|
||||
bash("curl -s -X POST ${SERVER}/session/${session_id}/abort", timeout=15000)
|
||||
# Delete the session entirely to avoid clutter
|
||||
bash("curl -s -X DELETE ${SERVER}/session/${session_id}", timeout=15000)
|
||||
stale_count += 1
|
||||
# Step 3: Check status of each existing session
|
||||
# Build a map of which supervisors are already running
|
||||
existing_supervisors = {} # display_name -> session_id
|
||||
for line in EXISTING (one per line):
|
||||
name, session_id = line.split("=")
|
||||
STATUS = bash("curl -s ${SERVER}/session/status", timeout=30000)
|
||||
if session_id is active/running in STATUS:
|
||||
existing_supervisors[name] = session_id
|
||||
|
||||
# Step 4: Clean up tracking file from previous run
|
||||
bash("rm -f /tmp/ca-supervisor-sessions.env", timeout=5000)
|
||||
|
||||
# Step 5: Log cleanup results
|
||||
if stale_count > 0:
|
||||
if existing_supervisors:
|
||||
invoke ca-session-persister with:
|
||||
checkpoint: "Phase C.0: Cleaned up <stale_count> stale supervisor
|
||||
sessions from a previous interrupted run.
|
||||
All old supervisors aborted and deleted."
|
||||
else:
|
||||
# No stale sessions — first run or clean shutdown previously.
|
||||
pass
|
||||
checkpoint: "Phase C.0: Found <len(existing_supervisors)> existing
|
||||
supervisor sessions from a previous run. Adopting them.
|
||||
Running: <list of names>.
|
||||
Will launch only the missing supervisors."
|
||||
|
||||
# ── PHASE C.1: Planning (if needed) ─────────────────────────────
|
||||
# If there are milestones with no issues yet, plan them first.
|
||||
@@ -454,9 +448,15 @@ Pre-flight: Launching 11 supervisors via prompt_async:
|
||||
11. [ ] timeline-updater (ca-timeline-updater)
|
||||
|
||||
# ── Helper function: launch one supervisor ───────────────────────
|
||||
# For EACH supervisor, run this bash sequence:
|
||||
# For EACH supervisor, SKIP if already running (adopted in Phase C.0).
|
||||
|
||||
function launch_supervisor(agent_name, display_name, prompt_text):
|
||||
# Check if this supervisor was adopted from a previous run
|
||||
if display_name in existing_supervisors:
|
||||
# Already running — record its session ID and skip launch
|
||||
echo "${display_name}=${existing_supervisors[display_name]}" \
|
||||
>> /tmp/ca-supervisor-sessions.env
|
||||
return # Do NOT create a new session
|
||||
# Step 1: Create a session
|
||||
SESSION_ID=$(curl -s -X POST "${SERVER}/session" \
|
||||
-H "Content-Type: application/json" \
|
||||
@@ -919,6 +919,20 @@ session can resume:
|
||||
|
||||
---
|
||||
|
||||
## Bot Signature (Required on ALL Forgejo Content)
|
||||
|
||||
Every comment, issue body, PR description, and review you post to Forgejo
|
||||
MUST end with this signature block:
|
||||
|
||||
```
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Product Builder | Agent: product-builder
|
||||
```
|
||||
|
||||
Append this to the END of every piece of content you create on Forgejo.
|
||||
No exceptions — every comment, every issue body, every PR description.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- **Forgejo API unreachable**: Retry indefinitely with exponential backoff
|
||||
|
||||
Reference in New Issue
Block a user