agents: the ordered runbook for standing the Gitea surface up
WORKFLOW-gitea.md said what the working surface is and why. It did not say how, in what order, or how to know a step worked -- so it was a destination with no route. This is the route. Seven phases, each with a check, each marked 👤 human or 🤖 me: 1 identities two agent users, Write NOT Admin 2 protection main behind a PR + 1 approval -- BEFORE tokens exist 3 tokens three principals, three tokens, three files 4 structure labels and bundles, and deliberately NO Kanban board 5 MCP gitea-mcp v1.7.0, per-agent identity, user scope not .mcp.json 6 items migrate the live findings only -- not 1,227 historical lines 7 restart and verify the three things that must be true Phase 1 leads because it is not hygiene: Gitea does not let a PR's author approve it, so while an agent IS `fabi` either the human cannot approve its work or it can approve its own. The review gate does not exist until the agents are distinct people. (It also fixes 495 commits of agent work attributed to the human's email.) Phase 2's check is a real push and a real PR, not a reading of the settings page. The reason protection lives in the server rather than in a brief is that it should not depend on good behaviour -- so verifying it should not either. Phase 5's install facts are checked, not remembered: gitea-mcp v1.7.0, `gitea-mcp_Linux_x86_64.tar.gz`, `-t stdio -H <host>`, `GITEA_ACCESS_TOKEN`. The `--tools` filter is flagged as an EXPERIMENT that might exclude the merge tool as defence in depth -- explicitly not a substitute for phase 2. Ends with what is still to build (propose-work, an attachment uploader, gitea-verify, the wiki landing page) and a "what I have not verified" section: the approve-your-own-PR behaviour, the --tools names, the Projects API, and the Gitea version -- the API was unreachable from my sandbox three times running.
This commit is contained in:
230
docs/agents/GITEA-SETUP.md
Normal file
230
docs/agents/GITEA-SETUP.md
Normal file
@@ -0,0 +1,230 @@
|
||||
# Runbook: standing the Gitea working surface up
|
||||
|
||||
**For the human. Work top to bottom — later phases depend on earlier ones.**
|
||||
[`WORKFLOW-gitea.md`](WORKFLOW-gitea.md) says *what* this is and why; this says
|
||||
*how*, in order, with a check after each phase.
|
||||
|
||||
Steps are marked **👤 you** (a decision or a credential only you can make) or
|
||||
**🤖 me** (I do it once you have unblocked it).
|
||||
|
||||
## Where things stand
|
||||
|
||||
* ✅ `main` carries the play-tested port work and the rewritten briefs (`cd3a81af`).
|
||||
* ✅ The briefs already describe this workflow. **They describe tooling that does
|
||||
not exist yet** — which is why the agents are stopped and must stay stopped
|
||||
until Phase 7.
|
||||
* ❌ Nothing exists on the instance: no agent users, no API tokens, no labels, no
|
||||
branch protection, no MCP.
|
||||
|
||||
⚠️ **Do not start an agent before Phase 7.** Its brief will tell it to read
|
||||
notifications and open issues, and it will have no tool that can.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 · Identities 👤
|
||||
|
||||
The agents currently push as `fabi`, using your credential. That is the defect
|
||||
this phase fixes, and it is not cosmetic: **Gitea does not let the author of a
|
||||
pull request approve it.** While an agent *is* you, either you cannot approve its
|
||||
PR or it can approve its own — and there is no third possibility. The review gate
|
||||
does not exist until the agents are distinct people.
|
||||
|
||||
Two more reasons, once you are there anyway: 495 commits of decoder work are
|
||||
currently attributed to **your** email, so blame is wrong; and separate
|
||||
identities mean revoking one agent does not touch the other or you.
|
||||
|
||||
**1.1 — Create two users.** Site Administration → Identity & Access → User
|
||||
Accounts → *Create User Account*.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| usernames | `sylph-decoder`, `sylph-port` |
|
||||
| email | anything you control and can tell apart — `you+decoder@…`, `you+port@…` |
|
||||
| "require password change on first login" | **off** — they never log in interactively |
|
||||
|
||||
**1.2 — Add both to `fabi/Sylpheed` as collaborators.** Repo → Settings →
|
||||
Collaborators → add each → permission **Write**.
|
||||
|
||||
🔴 **Write, not Admin.** Admin can edit branch protection, which would let an
|
||||
agent remove the rule that stops it merging.
|
||||
|
||||
> **Check:** the repo's Collaborators list shows both, each reading `Write`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 · Branch protection 👤
|
||||
|
||||
**Do this before the agents hold tokens**, so there is no window in which they
|
||||
can push to `main`.
|
||||
|
||||
Repo → Settings → Branches → *Protected Branches* → add rule for `main`:
|
||||
|
||||
| setting | value | why |
|
||||
|---|---|---|
|
||||
| Enable Push | **off** | nothing reaches `main` except through a PR |
|
||||
| Require approvals | **1** | the human gate, made native |
|
||||
| Dismiss stale approvals | **on** | an approval must describe the code that merges |
|
||||
| Block merge on rejected reviews | **on** | "changes requested" has to mean something |
|
||||
|
||||
> ### Check — and actually run it, do not assume it
|
||||
>
|
||||
> The whole point of putting this in protection rather than in a document is
|
||||
> that it does not depend on anyone's good behaviour. So verify it the same way:
|
||||
>
|
||||
> 1. As `sylph-port`, push a throwaway branch and open a PR into `main`.
|
||||
> 2. Confirm **no Merge button** is offered to that account.
|
||||
> 3. Confirm **you** can approve it, and that *it* cannot approve itself.
|
||||
> 4. Close the PR, delete the branch.
|
||||
>
|
||||
> If step 2 offers a Merge button, stop — the rest of this runbook assumes it
|
||||
> does not.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 · Tokens 👤
|
||||
|
||||
Three principals, three tokens. Settings → Applications → *Generate New Token*
|
||||
while logged in **as that user**.
|
||||
|
||||
| whose | scopes | goes in |
|
||||
|---|---|---|
|
||||
| **you** (`fabi`) | `write:issue`, `read:repository` | `~/.sylph-gitea-api-token` |
|
||||
| `sylph-decoder` | `write:repository`, `write:issue`, `write:notification`, `read:user` | `~/.sylph-gitea-token-decoder` |
|
||||
| `sylph-port` | same four | `~/.sylph-gitea-token-port` |
|
||||
|
||||
```bash
|
||||
printf '%s\n' '<token>' > ~/.sylph-gitea-api-token && chmod 600 ~/.sylph-gitea-api-token
|
||||
```
|
||||
|
||||
⚠️ **Never paste a token into chat.** The files are mounted read-only into the
|
||||
containers, exactly like `~/.sylph-claude-token`.
|
||||
|
||||
📌 The existing `~/.sylph-git-credentials` is scoped `write:repository` and is
|
||||
**refused by every issue endpoint** — verified, not assumed:
|
||||
`required=[read:issue], token scope=write:repository`. It stays as it is; these
|
||||
are additional.
|
||||
|
||||
> **Check:** `tools/gitea-setup --dry-run` prints "would create …" rather than a
|
||||
> scope error.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 · Labels and bundles 🤖
|
||||
|
||||
```bash
|
||||
tools/gitea-setup --dry-run # read it first
|
||||
tools/gitea-setup # idempotent; safe to re-run
|
||||
```
|
||||
|
||||
Creates 12 labels (the `state/*` set, `agent/*`, `kind/*`) and 4 milestones
|
||||
(Menus, Title screen, Graphics pipeline, Infrastructure).
|
||||
|
||||
**No Kanban board yet, on purpose.** Gitea's board does not follow labels, so it
|
||||
would be a second copy of the state to keep in sync by hand — which is the exact
|
||||
failure that produced a 1,227-line `BLOCKED.md`. **Labels are the truth**; a
|
||||
saved issue filter gives the same view for nothing. Add a board later if the
|
||||
filter turns out to be insufficient.
|
||||
|
||||
> **Check:** the Issues page offers the `state/*` labels, and Milestones lists
|
||||
> the four bundles.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 · The MCP server 🤖
|
||||
|
||||
`gitea-mcp` **v1.7.0** — `gitea-mcp_Linux_x86_64.tar.gz` from
|
||||
`https://gitea.com/gitea/gitea-mcp/releases`. Confirmed flags: `-t stdio`,
|
||||
`-H <host>`, token via `GITEA_ACCESS_TOKEN`.
|
||||
|
||||
Three edits per image, which I make:
|
||||
|
||||
1. **`Dockerfile`** — fetch and unpack the release binary to `/usr/local/bin`,
|
||||
pinned to v1.7.0 with a checksum.
|
||||
2. **`entrypoint.sh`** — register it for that agent's own identity:
|
||||
```bash
|
||||
claude mcp add -s user gitea -e "GITEA_ACCESS_TOKEN=$(cat "$GITEA_TOKEN_FILE")" \
|
||||
-- gitea-mcp -t stdio -H https://git.mc02.dev
|
||||
```
|
||||
User scope, not a committed `.mcp.json` — the token differs per agent and none
|
||||
of it belongs in git.
|
||||
3. **`sylph-decoder` / `sylph-port`** — mount the matching token file read-only.
|
||||
|
||||
Then rebuild both images. ⚠️ `CARGO_BUILD_JOBS=4` and limited `-j`; a
|
||||
full-parallel build has OOM-crashed this box.
|
||||
|
||||
📌 **Worth trying, unverified:** the server takes `--tools` / `GITEA_TOOLS` to
|
||||
filter which tools it exposes. If a merge tool can be excluded by name, that is
|
||||
defence in depth behind Phase 2 — belt *and* braces. I have not confirmed the
|
||||
tool names, so this is an experiment at install time, **not** a substitute for
|
||||
branch protection.
|
||||
|
||||
> **Check:** in each container, `claude mcp list` shows `gitea` connected, and a
|
||||
> read call returns this repo's labels.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 · Seed the first items 🤖 + 👤
|
||||
|
||||
I migrate the live findings into issues — **not** the 1,227 historical lines,
|
||||
only what is actually open:
|
||||
|
||||
| bundle | items |
|
||||
|---|---|
|
||||
| **Title screen** | F5 (does Ⓐ snap or accelerate?), F6 (`ptloop01/02` sweep onset), re-propose the F5/F6 work left off `main` |
|
||||
| **Menus** | F1 (held-direction repeat rate — Decoder measures, Port implements), F2 (SFX mix too loud), F3 (missing title audio), re-propose the OPTIONS menu work |
|
||||
|
||||
Each gets a bundle, an owner label, a dependency edge where one waits on the
|
||||
other, and — for anything already written on the port branch — a note that the
|
||||
code exists and needs re-proposing as a reviewable PR, not rewriting.
|
||||
|
||||
**👤 Your part:** approve the *shape* of each (`state/proposed` →
|
||||
`state/approved`). This is the cheap gate — before effort, not after.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 · Restart, and verify the loop 🤖 + 👤
|
||||
|
||||
```bash
|
||||
docker/decoder/sylph-decoder
|
||||
docker/port/sylph-port
|
||||
```
|
||||
|
||||
> ### Check — the three things that must be true
|
||||
>
|
||||
> 1. Each agent's **first iteration reads its notifications.** If it does not,
|
||||
> nothing addressed to it will ever arrive: **notifications are polled, and
|
||||
> nothing pushes.**
|
||||
> 2. Each opens a **pull request**, not a bare branch push, and labels its issue
|
||||
> `state/needs-human` with a one-line "look at this".
|
||||
> 3. Neither can merge. (Already proven in Phase 2; confirm it holds for a real
|
||||
> PR.)
|
||||
|
||||
---
|
||||
|
||||
## Still to build 🤖
|
||||
|
||||
Not blockers for Phase 7, but the workflow is not finished without them:
|
||||
|
||||
* **`propose-work`**, superseding `push-work` — push the branch *and* open the PR
|
||||
with `Closes #N` *and* set the label, in one step. Today `push-work` does the
|
||||
first third; the other two thirds being manual is how they get skipped. Its
|
||||
existing refusals stay: no `main`, no force-push.
|
||||
* **an attachment uploader** — the MCP exposes `attachment_read` only, so putting
|
||||
a screenshot on an issue needs a direct `POST /repos/{owner}/{repo}/issues/{index}/assets`.
|
||||
* **`gitea-verify`** — asserts protection is still on and the agents are still
|
||||
Write-not-Admin. Phase 2 is checked once; this checks it every day.
|
||||
* **wiki landing page** — bundles in flight and what each agent is on. There is
|
||||
currently no view of what is happening except container logs.
|
||||
|
||||
## What I have not verified
|
||||
|
||||
Said plainly, because a runbook that hides its soft spots is worse than one that
|
||||
does not:
|
||||
|
||||
* **that Gitea hides Approve from a PR's own author.** Widely true; Phase 2's
|
||||
check tests it directly rather than trusting me.
|
||||
* **the `--tools` filter names** (Phase 5) — an experiment, not a control.
|
||||
* **Gitea's Projects API**, which is why Phase 4 creates no board.
|
||||
* **the exact Gitea version** — the API was unreachable from my sandbox on the
|
||||
last three attempts. Every screen named here has been stable for many
|
||||
releases, but if a menu is not where I say it is, that is why.
|
||||
@@ -4,6 +4,10 @@
|
||||
person directs this project, and replaces `BLOCKED.md` as the way agents track
|
||||
what is open.
|
||||
|
||||
📌 This page is the **what and why**. The ordered **how** — users, branch
|
||||
protection, tokens, MCP, and the check after each step — is
|
||||
[`GITEA-SETUP.md`](GITEA-SETUP.md).
|
||||
|
||||
## Why not a new tool
|
||||
|
||||
We looked. The market has converged on **removing the human from the loop** —
|
||||
|
||||
Reference in New Issue
Block a user