# Universal Agent Rules

These rules apply to **every** agent in the CleverAgents system without exception.
No agent, at any tier or role, is exempt from any of these rules.


## Rule 1 — Exhaustive Pagination

Every tool call, REST/curl request, or any other command that returns a list
must be treated as potentially paginated and incomplete.

**Required protocol:**

1. Always set `limit` to its maximum available value:
   - Forgejo MCP tools: `limit=50`
   - Direct REST/curl calls: `limit=50` or higher
2. After each list response, count the returned items.
3. If the count equals `limit`, fetch the next page (`page=N+1`).
4. Continue until receiving a page with fewer items than `limit`.
5. Concatenate all pages before processing. Never act on a partial set.

This rule applies to **every** list-returning call without exception:
Forgejo MCP tools, curl REST calls, bash commands (`find`, `git log`,
`git branch -r`, etc.), and any other list-producing operation.

**Partial-page stop condition (exact):**

```
returned_count < limit  →  this is the last page, stop
returned_count == limit →  there may be more, fetch page+1
```

**Bash pagination pattern:**

```bash
PAGE=1
ALL_ITEMS=()
while true; do
  BATCH=$(curl -s "${FORGEJO_URL}/api/v1/some/endpoint?limit=50&page=${PAGE}" \
    -H "Authorization: token ${FORGEJO_PAT}")
  # process BATCH items...
  COUNT=$(echo "$BATCH" | jq length)
  [ "$COUNT" -lt 50 ] && break
  PAGE=$((PAGE + 1))
done
```

**Why this matters:** Missing a page means silently skipping work items,
causing incorrect escalation decisions, duplicate filings, or incomplete
audits. The cost of an extra API call is always less than the cost of
a missed item.


## Rule 2 — Label Management via `forgejo-label-manager`

**All label operations must go through the `forgejo-label-manager` subagent.**

### Forbidden operations — no agent may use these directly:

| Forbidden | Reason |
|-----------|--------|
| `forgejo_add_issue_labels` | Direct label application bypasses validation |
| `forgejo_edit_issue` with label fields | Direct mutation bypasses label manager |
| `forgejo_create_label` | Labels are never created by agents during operation |
| `forgejo_create_org_label` | Same — never create labels |
| `forgejo_create_repo_label` | Same — never create labels |
| `forgejo_list_repo_labels` | Must use org-level labels only; repo-level labels not authoritative |
| Any curl `POST/PUT/PATCH` to label endpoints | Bypasses forgejo-label-manager |

### Required pattern:

```
Need to apply, remove, or check labels?
→ Invoke forgejo-label-manager subagent
→ Pass: issue/PR number, repo info, desired label names, operation type
→ forgejo-label-manager validates org-level existence, then applies via PUT (replace-all)
```

### Why PUT (replace-all) not POST (add)?

Exclusive labels (e.g. `State/Open`, `State/Closed`) are mutually exclusive
within their prefix group. Using POST to add a label leaves conflicting labels
behind. `forgejo-label-manager` always uses PUT to safely replace the full set.

### Org-level labels only:

All labels applied by agents must exist at the **organization level**. Labels
are defined once at the org level and inherited by all repos. Repo-level labels
are not used by the autonomous agent system.


## Rule 3 — Bot Signature on All Forgejo Content

Every piece of content created on Forgejo by an agent — issue bodies, PR
descriptions, comments — must end with this exact signature block.

### Pool supervisor workers:

```
---
**Automated by CleverAgents Bot**
Supervisor: [Your Pool Name] | Agent: [your-agent-name]
```

### Utility subagents (no pool):

```
---
**Automated by CleverAgents Bot**
Agent: [your-agent-name]
```

**Placement:** Always the **last content** in the body — after all other text,
after code blocks, after any closing notes. Never buried in the middle.

**Values:** Use the values given in your system prompt. Do not invent or guess
the pool name or agent name — they are always provided in context.


## Rule 4 — Credential Flow: Workers Never Read Environment Variables

All credentials flow **downward through prompt text only**. No agent below
`product-builder` may read environment variables.

### The hierarchy:

```
product-builder
  → reads: FORGEJO_PAT, FORGEJO_USERNAME, FORGEJO_PASSWORD,
           FORGEJO_REVIEWER_PAT, FORGEJO_REVIEWER_USERNAME, FORGEJO_REVIEWER_PASSWORD,
           GIT_USER_NAME, GIT_USER_EMAIL, FORGEJO_URL, FORGEJO_OWNER, FORGEJO_REPO,
           CA_MAX_PARALLEL_WORKERS
  → stores as local variables for the session

product-builder launches supervisors
  → embeds ALL credentials explicitly in each supervisor's prompt text

supervisors launch workers
  → embed ALL credentials explicitly in each worker's prompt text

workers use credentials
  → from prompt text only — never from os.environ, never from echo $VAR
```

### Every worker prompt MUST include:

- Repository owner and name
- `FORGEJO_URL`
- `FORGEJO_PAT` (or reviewer PAT if this is a review worker)
- `FORGEJO_USERNAME`
- `FORGEJO_PASSWORD`
- `GIT_USER_NAME`
- `GIT_USER_EMAIL`
- Any other credentials the specific task requires

**If a supervisor omits any of these, the worker cannot proceed.** Supervisors
bear full responsibility for including complete credentials in every worker prompt.


## Rule 5 — localhost:4096 Restriction

Only `async-agent-util` is permitted to make HTTP calls to the OpenCode
server at `localhost:4096` (or `127.0.0.1:4096`).

### What this means in practice:

| Agent type | Permitted action |
|------------|-----------------|
| `async-agent-util` | Direct HTTP to `localhost:4096` — this is its sole purpose |
| All other agents | **FORBIDDEN** from calling `localhost:4096` under any circumstances |

### Required pattern for all non-manager agents:

```
Need to launch, monitor, or terminate an async agent session?
→ Invoke async-agent-util via the Task tool
→ Pass the operation name and all parameters in the prompt
→ async-agent-util makes the localhost:4096 call on your behalf
→ async-agent-util returns the result to you
```

### Operations async-agent-util handles:

| Operation | What it does |
|-----------|-------------|
| Create session | `POST /session` — creates a new named session |
| `prompt_async` | `POST /session/{id}/prompt_async` — fire-and-forget agent launch (returns 204) |
| Get status | `GET /session/{id}` — check if session is `busy` or `idle` |
| Get messages | `GET /session/{id}/message` — retrieve agent output |
| Delete session | `DELETE /session/{id}` — terminate and clean up |

**Why the restriction?** `localhost:4096` is the OpenCode server control plane.
Unrestricted access from any agent would create uncontrolled recursive session
spawning, resource exhaustion, and untraceable agent genealogies. Channeling
all access through `async-agent-util` provides a single audit point and
prevents these failure modes.

**Violating this rule is a critical architecture violation** and will be
detected by system-watchdog's deep session inspection.
