Files
agents/README.md
T
Felix FaerberandClaude Opus 4.8 9b9e1e945d
ci / lint (push) Skipped
ci / lint (pull_request) Successful in 11s
agents: fix review findings — thread attribution, races, rescue stall, docs, CI
Fixes from a full repo review:

- fetch-thread.sh: attribute every comment to its REAL author (@pm/@qa/… —
  agents post as their own users now); the old "🤖 @name line at the top" hint
  pointed at headers we removed, leaving every teammate comment anonymous. Also
  strip the hidden loop-prevention marker from bodies (prompt noise).
- agent.yml: per-issue concurrency group (queued, no cancel) — two quick
  comments on one issue no longer race the same ai/issue-N branch.
- rescue-pr.sh: after opening a rescue PR, hand it back into the flow with an
  unmarked @pm trigger (the pm→qa choreography previously stalled silently on
  any rescued run); drop the old "🤖 **@name**" header style; add ops to the
  token case; mark the status note with the hidden marker.
- README: token table said "@qa merges / TOKEN_QA needs write:repository" —
  inverted since the PM-orchestrated flow (@pm merges, autopilot only; @qa is
  read-only). Updated the agent table (descs, node1-ssh moved to homelab) and
  added a "How a task flows" section.
- agents.json: pm/qa descs now describe the orchestrator/reviewer roles (these
  feed the roster prompt agents route by).
- NEW ci.yml: bash -n + shellcheck(-S error) on every script, YAML-parse on
  every workflow, agents.json schema check — the ${x:-{}} brace bug would have
  been caught here before it shipped.
- install-opencode.sh: pin opencode (default 1.17.13, override via
  OPENCODE_VERSION) — a breaking release no longer takes down every agent.
- build-activity-log.sh: ollama/ollama-cloud models are subscription-billed
  (no $/token exists) — label the footer "subscription" instead of a
  misleading $0.0000; metered (anthropic) models keep the real dollar cost.
- route.sh: document that mention-priority is list-order and load-bearing for
  the flow's trigger comments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 09:47:34 +03:00

7.9 KiB

agents

Shared AI dev-team workflow for Gitea Actions, reusable across repos. It gives any repo the @pm / @junior / @senior / @lead / @qa agents driven from issues and comments.

Agents

Agent Model Vision Mode Skills Role
@pm ollama-cloud/gemma4:cloud yes comment gitea-api Product manager & orchestrator — plans, picks the dev, hands finished PRs to @qa, reports back to the issue creator (autopilot: merges approved PRs itself). Issue thread only; never edits files, never reads the PR diff.
@junior ollama-cloud/kimi-k2.7-code:cloud no pr Junior dev — small, low-risk changes (mostly YAML/compose/config). Text-only, cannot read images. Defers complex or image tasks to @senior or @lead.
@senior ollama-cloud/glm-5.2:cloud no pr gitea-api Senior dev — complex, multi-file implementation (GLM-5.2 via Ollama Cloud, text-only).
@lead anthropic/claude-opus-4-8 yes pr gitea-api Tech lead — the hardest problems, architecture, and final calls.
@qa ollama-cloud/minimax-m3:cloud yes comment gitea-api QA / reviewer — reads the PR diff, drives a headless browser (Playwright) to verify behavior; recommendations on the PR, pass/fail verdict on the issue. Never edits code, never merges.
@ops anthropic/claude-opus-4-8 no comment gitea-admin Gitea operator — administers the instance itself (create orgs/users/repos, labels, secrets, scoped per-user tokens, bootstrap repos). Comments only; never edits code. Confirms before destructive actions.

The registry .gitea/workflows/scripts/agents.json is the source of truth for this mapping — if you change a model or an agent's skills there, update this table too. (Repo-specific skills, e.g. a deploy-host SSH skill, live in the consuming repo under .gitea/agent-skills/ — not in this table.)

How a task flows

@pm orchestrates from the issue thread; the review happens on the PR; @pm never reads the PR (keeps its context small) and @qa never merges.

  1. Issue opened@pm plans and names a dev, then asks the creator "ready? reply yes" (with the autopilot label it skips the question and delegates immediately).
  2. Dev builds on ai/issue-N, a PR opens automatically, and the dev pings @pm on the issue.
  3. @pm hands the PR to @qa.
  4. @qa reviews on the PR — either recommendations + BOUNCE: @dev (dev fixes → @qa re-verifies, direct loop, max 3 rounds) or APPROVE.
  5. On approval @qa posts the verdict on the issue@pm tells the creator "ready to merge" and a human merges — or, with the autopilot label, @pm merges and closes the issue itself.

@pm is the only agent that ever merges, and only under the autopilot label (its kill switch: remove the label mid-flight and the next step reverts to human control).

Per-agent skill scoping

Skills load on-demand: only a skill's one-line description ever appears in an agent's <available_skills> list, and the full SKILL.md body (curl/API how-to) is fetched only when the agent calls the skill tool — it is never baked into any system prompt. On top of that, each agent's skills list in the registry drives an OpenCode permission.skill block that denies all skills by default and allows only the listed ones. A denied skill is hidden entirely (its name and description are omitted), so e.g. @junior never sees gitea-api — it just knows from the roster that @senior/@lead can reach the Gitea API and asks them to. This keeps the "how it's done" detail out of agents that shouldn't act on it while still letting them know the capability exists.

Use it in a repo

The standard caller is one file, identical in every repo. Copy this repo's own .gitea/workflows/ai-agent.yml verbatim into the consuming repo — it is the source of truth, and agents itself uses the same file:

name: ai-agent
run-name: "ai-agent · #${{ github.event.issue.number }}"   # quotes required: bare # starts a YAML comment
# Standard caller for the shared AI-agent workflow (gitea/agents). Copy this file VERBATIM into
# any repo that should get the agents — it is identical in every repo. All logic + scripts live in
# agents/.gitea/workflows/; scripts are fetched from @main at run time. The `jobs.agent` wrapper is
# required: a reusable (workflow_call) workflow can only be invoked from a caller job, not top-level.
# `run-name` titles each run by the triggering issue (e.g. "ai-agent · #42") in the Actions list.
on:
  issue_comment:
    types: [created]
  issues:
    types: [opened]
jobs:
  agent:
    uses: gitea/agents/.gitea/workflows/agent.yml@main
    secrets: inherit

That's the whole per-repo footprint, and it's the minimum a caller can be: the on: triggers must live in each repo (a reusable workflow can't declare its callers' triggers) and the jobs.agent wrapper is mandatory for workflow_call. Everything else (agent registry, routing, delegation, reactions, PR/issue plumbing) lives here in agent.yml.

Repo layout

agent.yml is kept thin: each step's shell lives in its own file under .gitea/workflows/scripts/ (route.sh, install-opencode.sh, skill-node1-ssh.sh, skill-gitea-api.sh, fetch-images.sh, fetch-thread.sh, run-agent.sh, build-activity-log.sh, publish.sh), invoked as bash "$SCRIPTS/<name>.sh".

Because this is a reusable workflow (workflow_call), a caller run checks out the caller's repo, not this one — so those script files aren't on disk by default. agent.yml therefore checks this repo out into .agents-workflow/ (pinned to @main, matching the caller's uses: …@main) and points $SCRIPTS at it. Keep the workflow and its scripts moving together on main.

Required secrets (per repo, or org-level for all)

Secret For
ANTHROPIC_API_KEY @lead (and @pm/@senior/@qa if on Claude)
OLLAMA_URL, OLLAMA_CLOUD_API_KEY local ornith / Ollama Cloud (gemma4, kimi-k2.7-code, glm-5.2, minimax-m3)
TOKEN_PM,TOKEN_SENIOR,TOKEN_JUNIOR,TOKEN_LEAD,TOKEN_QA primary — each agent's own Gitea-user PAT. The running agent gets only its own token (as SELF_TOKEN) so it posts, commits and comments as itself, and its gitea-api skill acts with its own scopes. Scopes: devs + TOKEN_PM carry write:repository (@pm is the only agent that merges, autopilot only); TOKEN_QA is read:repository + write:issue (reviews, never merges).
TOKEN_OPS @ops only — the admin PAT behind the gitea-admin skill (create orgs/users/repos, manage labels & secrets, mint scoped tokens). Injected into the agent process only when the agent is @ops.

Each agent authenticates as itself: the Run-agent step selects that agent's TOKEN_* into SELF_TOKEN (never another agent's), and publish.sh uses the same token for the trigger comments that drive the flow (delegation, @qa hand-offs, bounces) and for @pm's autopilot merge — the two things the built-in GITEA_TOKEN can't do (it won't start new runs, and a merge under it won't fire downstream deploys). So every consuming repo must carry the per-agent TOKEN_* secrets (org-level for gitea/*, user-level for ffaerber/*); there is no shared fallback token. GITEA_TOKEN is auto-provided (used for reads). Tip: set the TOKEN_* once at the org / user level so every repo inherits them via secrets: inherit.

Also add to each consuming repo

  • AGENTS.md — the repo's own conventions (copy AGENTS.template.md from here and adapt). The agent reads the caller repo's AGENTS.md, so each repo can differ.
  • The bot users (pm,senior,…) as collaborators (needed on private repos, and enables @name autocomplete).

Maintaining

Change agent behavior once, here. Callers pin @main (or pin a tag for stability). History is the changelog — see git log.