forked from cleveragents/cleveragents-core
build(agents): add proposal issue gate before spec and agent-definition PRs
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).
This commit is contained in:
@@ -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-<agent_name>-<brief_slug>"
|
||||
git checkout -b <branch>
|
||||
|
||||
# Read the current agent definition
|
||||
agent_file = ".opencode/agents/<agent_name>.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 <agent_file>
|
||||
git commit -m "chore(agents): improve <agent_name> — <brief description>
|
||||
|
||||
Agent evolver identified a systematic pattern:
|
||||
- Pattern: <pattern.type>
|
||||
- Evidence: <pattern.evidence summary>
|
||||
- Fix: <pattern.suggestion>
|
||||
|
||||
This change requires human approval before taking effect."
|
||||
|
||||
# Push
|
||||
git push origin <branch>
|
||||
|
||||
# Create PR with `needs feedback` label
|
||||
create PR via Forgejo API:
|
||||
title: "chore(agents): improve <agent_name> — <brief description>"
|
||||
create Forgejo issue via API:
|
||||
title: "Proposal: improve <agent_name> — <brief description>"
|
||||
body: |
|
||||
## Agent Improvement Proposal
|
||||
|
||||
@@ -256,7 +225,9 @@ LOOP:
|
||||
**Evidence**: <detailed evidence with specific examples>
|
||||
|
||||
### Proposed Change
|
||||
<description of what was changed and why>
|
||||
<description of what would be changed and why — in prose,
|
||||
NOT as a code diff. The actual implementation happens only
|
||||
after this proposal is approved.>
|
||||
|
||||
### Expected Impact
|
||||
<what improvement this should produce>
|
||||
@@ -265,35 +236,136 @@ LOOP:
|
||||
<potential downsides or unintended consequences>
|
||||
|
||||
---
|
||||
*This PR was created by the agent evolver. It requires
|
||||
human review and approval before merge.*
|
||||
base: master
|
||||
head: <branch>
|
||||
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-<agent_name>-<brief_slug>"
|
||||
git checkout -b <branch>
|
||||
|
||||
agent_file = ".opencode/agents/<agent_name>.md"
|
||||
current_content = read agent_file
|
||||
modified_content = apply_targeted_fix(current_content, pattern)
|
||||
write modified_content to agent_file
|
||||
|
||||
git add <agent_file>
|
||||
git commit -m "chore(agents): improve <agent_name> — <brief description>
|
||||
|
||||
Approved proposal: #<issue_number>
|
||||
Pattern: <pattern.type>
|
||||
Evidence: <pattern.evidence summary>
|
||||
Fix: <pattern.suggestion>
|
||||
|
||||
ISSUES CLOSED: #<issue_number>"
|
||||
|
||||
git push origin <branch>
|
||||
|
||||
create PR via Forgejo API:
|
||||
title: "chore(agents): improve <agent_name> — <brief description>"
|
||||
body: |
|
||||
## Agent Improvement Implementation
|
||||
|
||||
Implements approved proposal #<issue_number>.
|
||||
|
||||
### Changes Made
|
||||
<description of the actual code change>
|
||||
|
||||
Closes #<issue_number>
|
||||
|
||||
---
|
||||
**Automated by CleverAgents Bot**
|
||||
Supervisor: Agent Evolver | Agent: ca-agent-evolver
|
||||
base: master
|
||||
head: <branch>
|
||||
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 #<N> merged. Change to <agent>: <summary>"
|
||||
"Agent improvement PR #<N> merged. Change to <agent>: <summary>
|
||||
|
||||
---
|
||||
**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 #<N> rejected by human reviewer."
|
||||
"Agent improvement PR #<N> 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 <N>:
|
||||
- Patterns analyzed: <N>
|
||||
- Proposal issues created: <N>
|
||||
- Proposals approved: <N>
|
||||
- Proposals rejected: <N>
|
||||
- Improvement PRs created: <N>
|
||||
- PRs merged (accepted): <N>
|
||||
- PRs rejected: <N>"
|
||||
- PRs merged: <N>
|
||||
- PRs rejected: <N>
|
||||
|
||||
---
|
||||
**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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 — <brief summary>"`
|
||||
- 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-<milestone>-<short-description>`
|
||||
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-<milestone>-<short-description>`
|
||||
3. Commit the spec changes to that branch and push it.
|
||||
4. Create a Pull Request on Forgejo targeting `master`:
|
||||
- Title: `docs: update specification — <brief summary>`
|
||||
- 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 #<N>`).
|
||||
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: #<N> — <title>
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user