From 270160eca9a0b87cefe7925c2fe0a792201ac4ed Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Freeman Date: Thu, 2 Apr 2026 18:16:14 +0000 Subject: [PATCH] build(agents): add proposal issue gate before spec and agent-definition PRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both the spec-updater and agent-evolver previously skipped straight to creating PRs with 'needs feedback' for proposed changes. The user needs a two-step human-approval workflow: Step 1: Agent creates a PROPOSAL ISSUE with 'needs feedback' label describing what it wants to change and why. No branch, no code changes. Step 2: Human reviews the issue and approves it (by removing 'needs feedback', adding 'State/Verified', or commenting with approval). Step 3: Agent detects approval, normalizes labels, creates branch + PR (also with 'needs feedback') implementing the approved change. Step 4: Human reviews the PR and merges it. Changes across 3 agent definitions: - ca-agent-evolver: Step 4 split into proposal issue creation (Step 4) and approved-proposal implementation (Step 5). Added pending_proposals and pending_prs state tracking. Approval detected via 3 signals: label removal, State/Verified addition, or human approval comment. - ca-spec-updater: Removed the minor/major classification and the "commit directly to master" path. ALL spec changes now go through proposal issues first (Step 6a creates issue, Step 7 monitors for approval). Added pending_spec_proposals and rejected_proposals state. Approval check runs every cycle before checking for new merged PRs. - ca-human-liaison: Added Step 4 guard — issues with 'needs feedback' label are NOT auto-verified. The liaison acknowledges them with a comment but does not change state labels. Only issues WITHOUT 'needs feedback' proceed to the normal auto-verify flow (now Step 5). --- .opencode/agents/ca-agent-evolver.md | 178 +++++++++++++++++++-------- .opencode/agents/ca-human-liaison.md | 25 +++- .opencode/agents/ca-spec-updater.md | 118 +++++++++++++----- 3 files changed, 232 insertions(+), 89 deletions(-) diff --git a/.opencode/agents/ca-agent-evolver.md b/.opencode/agents/ca-agent-evolver.md index 1eacd836..03ecc344 100644 --- a/.opencode/agents/ca-agent-evolver.md +++ b/.opencode/agents/ca-agent-evolver.md @@ -88,6 +88,8 @@ set timeout explicitly. You MUST NOT voluntarily exit — sleep and re-analyze. cycle = 0 proposed_changes = set() # Track what we've already proposed (avoid duplicates) rejected_changes = set() # Track changes rejected by humans (don't re-propose) +pending_proposals = {} # pattern_signature -> issue_number (awaiting human approval) +pending_prs = {} # pattern_signature -> pr_number (approved, PR awaiting merge) stale_count = 0 LOOP: @@ -207,46 +209,13 @@ LOOP: stale_count = 0 - # ── Step 4: Propose changes via PR ─────────────────────────── + # ── Step 4: Create PROPOSAL ISSUES (not PRs) ────────────────── + # For each actionable pattern, create a Forgejo ISSUE describing + # the proposed change. Do NOT create a branch or PR yet — that + # only happens after a human approves the proposal. for pattern in actionable: - # Update clone to latest master - cd "$CLONE_DIR" - git fetch origin - git checkout master - git reset --hard origin/master - - # Create a branch for this improvement - branch = "improvement/agent--" - git checkout -b - - # Read the current agent definition - agent_file = ".opencode/agents/.md" - current_content = read agent_file - - # Draft the modification - # Be SURGICAL — change only what's needed to address the pattern - modified_content = apply_targeted_fix(current_content, pattern) - - # Write the modified file - write modified_content to agent_file - - # Commit - git add - git commit -m "chore(agents): improve - - Agent evolver identified a systematic pattern: - - Pattern: - - Evidence: - - Fix: - - This change requires human approval before taking effect." - - # Push - git push origin - - # Create PR with `needs feedback` label - create PR via Forgejo API: - title: "chore(agents): improve " + create Forgejo issue via API: + title: "Proposal: improve " body: | ## Agent Improvement Proposal @@ -256,7 +225,9 @@ LOOP: **Evidence**: ### Proposed Change - + ### Expected Impact @@ -265,35 +236,136 @@ LOOP: --- - *This PR was created by the agent evolver. It requires - human review and approval before merge.* - base: master - head: - labels: ["needs feedback", "Type/Task"] + *This is a proposal from the agent evolver. A human must + approve this issue before the change will be implemented. + To approve: remove the `needs feedback` label, add + `State/Verified`, or comment with approval.* + --- + **Automated by CleverAgents Bot** + Supervisor: Agent Evolver | Agent: ca-agent-evolver + labels: ["needs feedback", "Type/Task", "State/Unverified"] + + pending_proposals[pattern.signature] = issue.number proposed_changes.add(pattern.signature) - # ── Step 5: Monitor existing improvement PRs ───────────────── + # ── Step 5: Check for APPROVED proposals ───────────────────── + # For each pending proposal issue, check if a human approved it. + # Approval signals (ANY of these): + # - "needs feedback" label was REMOVED + # - "State/Verified" label was ADDED + # - A human (non-bot) commented with approval language + # ("approved", "LGTM", "go ahead", "looks good", "yes") + for sig, issue_number in list(pending_proposals.items()): + issue = query Forgejo for issue #issue_number + labels = [l.name for l in issue.labels] + comments = fetch issue comments + + approved = false + if "needs feedback" not in labels: + approved = true + if "State/Verified" in labels: + approved = true + for comment in comments: + if comment.user is not bot and + any word in comment.body.lower() matches + ("approved", "lgtm", "go ahead", "looks good", "yes, proceed"): + approved = true + + if approved: + # Normalize labels: ensure State/Verified, remove needs feedback + remove label "needs feedback" (if present) + remove label "State/Unverified" (if present) + add label "State/Verified" + add label "State/In Progress" + + # NOW implement the change: branch, modify, commit, PR + cd "$CLONE_DIR" + git fetch origin + git checkout master + git reset --hard origin/master + + branch = "improvement/agent--" + git checkout -b + + agent_file = ".opencode/agents/.md" + current_content = read agent_file + modified_content = apply_targeted_fix(current_content, pattern) + write modified_content to agent_file + + git add + git commit -m "chore(agents): improve + + Approved proposal: # + Pattern: + Evidence: + Fix: + + ISSUES CLOSED: #" + + git push origin + + create PR via Forgejo API: + title: "chore(agents): improve " + body: | + ## Agent Improvement Implementation + + Implements approved proposal #. + + ### Changes Made + + + Closes # + + --- + **Automated by CleverAgents Bot** + Supervisor: Agent Evolver | Agent: ca-agent-evolver + base: master + head: + labels: ["needs feedback", "Type/Task"] + + pending_prs[sig] = pr.number + del pending_proposals[sig] + + elif issue.state == "closed": + # Human closed the proposal without approving — rejected + rejected_changes.add(sig) + del pending_proposals[sig] + + # ── Step 6: Monitor existing improvement PRs ───────────────── existing_prs = query Forgejo for PRs from improvement/* branches for pr in existing_prs: if pr.state == "closed" and pr.merged: - # Change was accepted! Log success. post comment on session state issue: - "Agent improvement PR # merged. Change to : " + "Agent improvement PR # merged. Change to : + + --- + **Automated by CleverAgents Bot** + Supervisor: Agent Evolver | Agent: ca-agent-evolver" elif pr.state == "closed" and not pr.merged: - # Change was rejected. Record to avoid re-proposing. rejected_changes.add(extract_pattern_signature(pr)) post comment on session state issue: - "Agent improvement PR # rejected by human reviewer." + "Agent improvement PR # rejected by human reviewer. - # ── Step 6: Post progress ──────────────────────────────────── + --- + **Automated by CleverAgents Bot** + Supervisor: Agent Evolver | Agent: ca-agent-evolver" + + # ── Step 7: Post progress ──────────────────────────────────── if cycle % 3 == 0: post comment on session state issue: "Agent evolver cycle : - Patterns analyzed: + - Proposal issues created: + - Proposals approved: + - Proposals rejected: - Improvement PRs created: - - PRs merged (accepted): - - PRs rejected: " + - PRs merged: + - PRs rejected: + + --- + **Automated by CleverAgents Bot** + Supervisor: Agent Evolver | Agent: ca-agent-evolver" # Sleep before next cycle. MUST use Bash tool: bash("sleep 1800", timeout=2400000) # 30 min sleep, 40 min timeout diff --git a/.opencode/agents/ca-human-liaison.md b/.opencode/agents/ca-human-liaison.md index b6903bae..523a71dd 100644 --- a/.opencode/agents/ca-human-liaison.md +++ b/.opencode/agents/ca-human-liaison.md @@ -266,9 +266,28 @@ Based on the issue content: - Suggest `Priority/*` based on impact analysis - Identify the appropriate milestone -### 4. Verify (Full Authority) +### 4. Check for `needs feedback` Label — DO NOT AUTO-VERIFY -If the issue is well-formed and clearly actionable: +**CRITICAL**: If the issue has the `needs feedback` label, it is a +**proposal awaiting human review** (from the agent-evolver or spec-updater). +You MUST NOT auto-verify it or change its state. Instead: + +- Post an acknowledgment comment: + ``` + This issue is a proposal awaiting human review (`needs feedback` label). + I will not modify its state — a human must approve or reject it. + + --- + **Automated by CleverAgents Bot** + Supervisor: Human Liaison | Agent: ca-human-liaison + ``` +- Skip all further triage steps for this issue +- Do NOT assign milestone, priority, or change any labels + +### 5. Verify (Full Authority) — Only Issues WITHOUT `needs feedback` + +If the issue does NOT have `needs feedback` and is well-formed and clearly +actionable: - Transition from `State/Unverified` to `State/Verified` via `ca-issue-state-updater` - Assign to the appropriate milestone @@ -283,7 +302,7 @@ Issue verified and triaged: - **Next step**: This issue is now ready for implementation. ``` -### 5. DO NOT assign MoSCoW labels +### 6. DO NOT assign MoSCoW labels MoSCoW labels (`MoSCoW/Must Have`, etc.) are set exclusively by the project owner per CONTRIBUTING.md. Never assign these. diff --git a/.opencode/agents/ca-spec-updater.md b/.opencode/agents/ca-spec-updater.md index 37a475e8..7bac5705 100644 --- a/.opencode/agents/ca-spec-updater.md +++ b/.opencode/agents/ca-spec-updater.md @@ -77,6 +77,8 @@ EXIT. You MUST NOT voluntarily exit — sleep and re-poll. last_master_sha = get current master HEAD cycle = 0 idle_cycles = 0 +pending_spec_proposals = {} # description_key -> issue_number (awaiting human approval) +rejected_proposals = set() # description keys of rejected proposals (don't re-propose) LOOP: cycle += 1 @@ -98,6 +100,37 @@ LOOP: idle_cycles = 0 last_master_sha = current_sha + # ── Check pending proposals for approval ───────────────────── + # Before looking for new work, check if any previous proposals + # have been approved by a human. + for desc_key, issue_number in list(pending_spec_proposals.items()): + issue = query Forgejo for issue #issue_number + labels = [l.name for l in issue.labels] + comments = fetch issue comments + + approved = false + if "needs feedback" not in labels: + approved = true + if "State/Verified" in labels: + approved = true + for comment in comments: + if comment.user is not bot and + any word in comment.body.lower() matches + ("approved", "lgtm", "go ahead", "looks good", "yes"): + approved = true + + if approved: + # Normalize labels + remove "needs feedback", "State/Unverified" (if present) + add "State/Verified", "State/In Progress" + # Implement: create branch, commit spec changes, create PR + # (follows Step 7 in the Process section) + implement_approved_spec_proposal(desc_key, issue_number) + del pending_spec_proposals[desc_key] + elif issue.state == "closed": + rejected_proposals.add(desc_key) + del pending_spec_proposals[desc_key] + # ── Check for recently merged PRs ──────────────────────────── recently_merged = query Forgejo for merged PRs since last check @@ -142,48 +175,67 @@ reverting deliberate decisions. 5. **Handle the monolithic→split transition** — if `docs/specification.md` exceeds ~3000 lines, restructure it into a `docs/specification/` directory with logical sub-documents (e.g., `architecture.md`, `data-model.md`, `api.md`, etc.) and a root `index.md` that links them together. Update any references elsewhere in the repo. -6. **Classify the scope of changes:** - - **Minor changes** (typo fixes, clarifications, formatting, updating descriptions - to match implementation without changing intent): these can be committed - directly to master. - - **Major changes** (new modules, altered interfaces, changed architectural - decisions, new milestones, removed or substantially rewritten sections): - these MUST go through a pull request with human approval. +6. **ALL spec changes go through proposal issues first.** No changes are + committed directly to master. Every spec modification — whether minor + (typo, clarification) or major (new modules, altered interfaces) — + follows this two-step human-approved workflow: -7. **For minor changes:** commit directly to master, push, and post a Forgejo - comment on the session state issue summarising what changed. + **Step 6a: Create a PROPOSAL ISSUE** (not a PR) describing the proposed + spec change: + - Title: `"Proposal: update specification — "` + - Labels: `needs feedback`, `Type/Task`, `State/Unverified` + - Body must include: + - **What changed in the implementation** (which merged PRs triggered this) + - **What spec section(s) need updating** (with current vs proposed text) + - **Rationale** for each change — why is the update needed + - **Scope**: list every spec section affected + - The bot signature block + - **Do NOT create a branch or PR yet.** Wait for human approval first. -8. **For major changes — Human-in-the-Loop PR workflow:** - 1. Create a new branch: `spec/update--` - 2. Commit the spec changes to that branch and push it. - 3. Create a Pull Request on Forgejo targeting `master`: + **Step 6b: Track the proposal** — add to `pending_spec_proposals` dict: + `pending_spec_proposals[description_key] = issue_number` + +7. **Monitor pending proposals each cycle.** In the main loop, before + checking for new merged PRs, check all pending proposal issues for + human approval signals: + + Approval is detected when ANY of these is true: + - `needs feedback` label was **removed** from the issue + - `State/Verified` label was **added** to the issue + - A human (non-bot user) commented with approval language + ("approved", "LGTM", "go ahead", "looks good", "yes") + + When a proposal is approved: + 1. Normalize labels: add `State/Verified` (if missing), remove + `needs feedback`, remove `State/Unverified`, add `State/In Progress` + 2. Create a new branch: `spec/update--` + 3. Commit the spec changes to that branch and push it. + 4. Create a Pull Request on Forgejo targeting `master`: - Title: `docs: update specification — ` - Body: detailed description of every change, the rationale for each, - and any open questions for the human reviewer. - 4. Add the label **`needs feedback`** to the PR. This label already exists - in the repository. - 5. **Do NOT merge this PR.** A human must review and merge it. - 6. Post a comment on the session state issue: + and reference to the approved proposal issue (`Closes #`). + 5. Add the label **`needs feedback`** to the PR. + 6. **Do NOT merge this PR.** A human must review and merge it. + 7. Post a comment on the session state issue: ``` - Spec update PR created: # - Label: needs feedback (awaiting human review) - Changes: <summary of changes> + Spec update proposal #<N> approved. PR created: #<PR_N> + Label: needs feedback (awaiting human review before merge) ``` - 7. Post a comment on the PR itself explaining the changes and why they - are being proposed, tagging it as awaiting human architectural review. - 8. Return the PR number so the product-builder can monitor it. -9. **Keep spec PRs up to date:** If asked to check on a previously-created - spec PR, verify it has not gone stale. If master has advanced since the PR + When a proposal is rejected (issue closed without approval): + - Record to avoid re-proposing the same change. + - Post a note on the session state issue. + +8. **Keep spec PRs up to date:** If master has advanced since a spec PR was created, rebase the spec branch onto master, force-push, and ensure - the PR is still mergeable. + the PR is still mergeable. Retry indefinitely with reclone fallback. -10. **Post a Forgejo comment** — on the session state issue, post a summary of: - - Spec sections updated (with brief rationale for each) - - Issues created for deviations - - Whether a monolithic→split restructure occurred - - For minor changes: the commit hash - - For major changes: the PR number and `needs feedback` status +9. **Post a Forgejo comment** — on the session state issue, post a summary of: + - Spec proposals created (with issue numbers) + - Proposals approved and PRs created + - Proposals rejected + - Issues created for incorrect deviations + - Whether a monolithic→split restructure was proposed ## CRITICAL: Preserve PR Body on Every Update