diff --git a/.opencode/agents/ca-agent-evolver.md b/.opencode/agents/ca-agent-evolver.md index 07da7358..1eacd836 100644 --- a/.opencode/agents/ca-agent-evolver.md +++ b/.opencode/agents/ca-agent-evolver.md @@ -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 diff --git a/.opencode/agents/ca-architecture-guard.md b/.opencode/agents/ca-architecture-guard.md index fa25784d..52bb3b25 100644 --- a/.opencode/agents/ca-architecture-guard.md +++ b/.opencode/agents/ca-architecture-guard.md @@ -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: diff --git a/.opencode/agents/ca-backlog-groomer.md b/.opencode/agents/ca-backlog-groomer.md index 63e43ecd..a302a53a 100644 --- a/.opencode/agents/ca-backlog-groomer.md +++ b/.opencode/agents/ca-backlog-groomer.md @@ -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. diff --git a/.opencode/agents/ca-bug-hunter.md b/.opencode/agents/ca-bug-hunter.md index af816589..c51e972b 100644 --- a/.opencode/agents/ca-bug-hunter.md +++ b/.opencode/agents/ca-bug-hunter.md @@ -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--" - - 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: \"}' \ + | 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: . max_workers: 1. \ + Repo: /. Forgejo PAT: . \ + Git: . 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 diff --git a/.opencode/agents/ca-continuous-pr-reviewer.md b/.opencode/agents/ca-continuous-pr-reviewer.md index e33cffaf..572ffa09 100644 --- a/.opencode/agents/ca-continuous-pr-reviewer.md +++ b/.opencode/agents/ca-continuous-pr-reviewer.md @@ -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 . - 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: / - - 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: / - - 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-\"}' \ + | 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 #. Repo: /. \ + Spec context: . \ + Forgejo PAT: . Git: . \ + \"}]}'", 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. diff --git a/.opencode/agents/ca-docs-writer.md b/.opencode/agents/ca-docs-writer.md index 3d4cf9fd..d2b45288 100644 --- a/.opencode/agents/ca-docs-writer.md +++ b/.opencode/agents/ca-docs-writer.md @@ -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. diff --git a/.opencode/agents/ca-epic-planner.md b/.opencode/agents/ca-epic-planner.md index 1a8abefd..236884c1 100644 --- a/.opencode/agents/ca-epic-planner.md +++ b/.opencode/agents/ca-epic-planner.md @@ -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: | 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: diff --git a/.opencode/agents/ca-human-liaison.md b/.opencode/agents/ca-human-liaison.md index b1158730..b6903bae 100644 --- a/.opencode/agents/ca-human-liaison.md +++ b/.opencode/agents/ca-human-liaison.md @@ -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 diff --git a/.opencode/agents/ca-issue-note-writer.md b/.opencode/agents/ca-issue-note-writer.md index baf21a84..65a0ea5e 100644 --- a/.opencode/agents/ca-issue-note-writer.md +++ b/.opencode/agents/ca-issue-note-writer.md @@ -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 diff --git a/.opencode/agents/ca-issue-state-updater.md b/.opencode/agents/ca-issue-state-updater.md index d48a998f..bbb77127 100644 --- a/.opencode/agents/ca-issue-state-updater.md +++ b/.opencode/agents/ca-issue-state-updater.md @@ -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. diff --git a/.opencode/agents/ca-issue-worker.md b/.opencode/agents/ca-issue-worker.md index e017773e..88ed31ad 100644 --- a/.opencode/agents/ca-issue-worker.md +++ b/.opencode/agents/ca-issue-worker.md @@ -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 diff --git a/.opencode/agents/ca-milestone-reviewer.md b/.opencode/agents/ca-milestone-reviewer.md index 2f3ee3de..0659281b 100644 --- a/.opencode/agents/ca-milestone-reviewer.md +++ b/.opencode/agents/ca-milestone-reviewer.md @@ -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 diff --git a/.opencode/agents/ca-new-issue-creator.md b/.opencode/agents/ca-new-issue-creator.md index caf64894..aa388c3f 100644 --- a/.opencode/agents/ca-new-issue-creator.md +++ b/.opencode/agents/ca-new-issue-creator.md @@ -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: | 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: diff --git a/.opencode/agents/ca-pr-api-creator.md b/.opencode/agents/ca-pr-api-creator.md index 84a341f3..053fbdc9 100644 --- a/.opencode/agents/ca-pr-api-creator.md +++ b/.opencode/agents/ca-pr-api-creator.md @@ -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 diff --git a/.opencode/agents/ca-pr-checker.md b/.opencode/agents/ca-pr-checker.md index 12ab1bed..d3c4e2f4 100644 --- a/.opencode/agents/ca-pr-checker.md +++ b/.opencode/agents/ca-pr-checker.md @@ -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. diff --git a/.opencode/agents/ca-pr-self-reviewer.md b/.opencode/agents/ca-pr-self-reviewer.md index 9c5d64a8..f3602aeb 100644 --- a/.opencode/agents/ca-pr-self-reviewer.md +++ b/.opencode/agents/ca-pr-self-reviewer.md @@ -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. diff --git a/.opencode/agents/ca-product-verifier.md b/.opencode/agents/ca-product-verifier.md index 60f49c76..7b60d5cc 100644 --- a/.opencode/agents/ca-product-verifier.md +++ b/.opencode/agents/ca-product-verifier.md @@ -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: diff --git a/.opencode/agents/ca-project-bootstrapper.md b/.opencode/agents/ca-project-bootstrapper.md index bf5def2d..bc598b0f 100644 --- a/.opencode/agents/ca-project-bootstrapper.md +++ b/.opencode/agents/ca-project-bootstrapper.md @@ -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: diff --git a/.opencode/agents/ca-session-cleanup.md b/.opencode/agents/ca-session-cleanup.md new file mode 100644 index 00000000..89ee8ba0 --- /dev/null +++ b/.opencode/agents/ca-session-cleanup.md @@ -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 automated sessions to clean up:" +for each line in AUTOMATED: + Output: " - | " + +# 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. diff --git a/.opencode/agents/ca-session-persister.md b/.opencode/agents/ca-session-persister.md index f4cddfc5..9a2fdcbe 100644 --- a/.opencode/agents/ca-session-persister.md +++ b/.opencode/agents/ca-session-persister.md @@ -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 diff --git a/.opencode/agents/ca-spec-updater.md b/.opencode/agents/ca-spec-updater.md index a14ea2fe..37a475e8 100644 --- a/.opencode/agents/ca-spec-updater.md +++ b/.opencode/agents/ca-spec-updater.md @@ -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. diff --git a/.opencode/agents/ca-subtask-checker.md b/.opencode/agents/ca-subtask-checker.md index 4aefedd1..85fcc7b2 100644 --- a/.opencode/agents/ca-subtask-checker.md +++ b/.opencode/agents/ca-subtask-checker.md @@ -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. diff --git a/.opencode/agents/ca-subtask-loop.md b/.opencode/agents/ca-subtask-loop.md index b001fdea..0c862f3c 100644 --- a/.opencode/agents/ca-subtask-loop.md +++ b/.opencode/agents/ca-subtask-loop.md @@ -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 diff --git a/.opencode/agents/ca-timeline-updater.md b/.opencode/agents/ca-timeline-updater.md index 1948258e..856902d4 100644 --- a/.opencode/agents/ca-timeline-updater.md +++ b/.opencode/agents/ca-timeline-updater.md @@ -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 diff --git a/.opencode/agents/ca-uat-tester.md b/.opencode/agents/ca-uat-tester.md index 36ac9652..e0ca3c94 100644 --- a/.opencode/agents/ca-uat-tester.md +++ b/.opencode/agents/ca-uat-tester.md @@ -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 diff --git a/.opencode/agents/issue-implementor.md b/.opencode/agents/issue-implementor.md index 3e820fa0..bc5e6b59 100644 --- a/.opencode/agents/issue-implementor.md +++ b/.opencode/agents/issue-implementor.md @@ -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. diff --git a/.opencode/agents/product-builder.md b/.opencode/agents/product-builder.md index e74c75f5..9ba514ff 100644 --- a/.opencode/agents/product-builder.md +++ b/.opencode/agents/product-builder.md @@ -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