Commit Graph

3 Commits

Author SHA1 Message Date
eccb789c0b docker: give each agent its own Gitea hands, and close the cross-approval hole
Phase 5 of docs/agents/GITEA-SETUP.md, plus a correction to Phase 2 that the
runbook could not have known it needed.

gitea-mcp v1.7.0 goes into both images, pinned by the sha256 the release
publishes and smoke-tested with `--version` at build time, so a bad pin fails
the build instead of the agent. Each entrypoint registers it at user scope for
that container's own identity, remove-then-add so a restart is idempotent.

The token is passed BY PATH. `-e GITEA_ACCESS_TOKEN=$(cat …)` would write it in
cleartext into ~/.claude.json, which every session in the container reads;
GITEA_ACCESS_TOKEN_FILE is new in the pinned version and leaves the secret in
its read-only mount. Verified against the binary's own --help, not assumed.

The tool filter stops being an experiment. The names are in the release README:
each agent gets issues, notifications, labels, milestones and pull requests, and
NOT `pull_request_review_write`. That one matters because separate identities
open a hole the runbook did not name: Gitea refuses to let an author approve
their own pull request, and does nothing about sylph-decoder approving
sylph-port's. Two agents could satisfy `required_approvals = 1` between
themselves and then merge, since branch protection blocks pushes to main and
never blocked merges.

Withholding the tool is defence in depth; the controls are in branch protection,
and both docs now say so: approvals whitelisted to the human so an agent's
approval does not count, merges whitelisted to the human so an approved PR is
still merged by a person. Phase 2's check gains the step that actually tests it
-- approve the throwaway PR yourself, then confirm the agent STILL has no merge
button. Without that step, the check passes on an instance where the agents can
merge each other's work.

Also settles two entries on the runbook's own "not verified" list: the tool
filter names, and the Gitea version (1.25.5, whose API schema carries
enable_merge_whitelist and enable_approvals_whitelist under those names).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01McNbzUeq1KRBWs4G6X2YVj
2026-09-04 16:39:12 +02:00
MechaCat02
9652a5ad77 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.
2026-09-04 16:25:10 +02:00
MechaCat02
ad96fe97b8 agents: move the working surface to Gitea -- issues, PRs, and where files live
The human wants to direct this project from a web UI rather than chat or Remote
Control, so Gitea becomes the working surface. No new store: adding a second
copy of the truth is this project's defining failure mode, and Gitea already
holds the code. Its first-party MCP server (gitea/gitea-mcp v1.7.0, checked) has
issues, labels, milestones, PRs, attachments and notifications.

ISSUES replace BLOCKED.md. Milestones are bundles the human defines; issues are
items agents propose and the human approves. The state labels end in
`needs-human`, which is the state the whole model turns on and the one no
off-the-shelf tool models -- the market has converged on removing the human.
`blocked` uses Gitea's DEPENDENCY EDGES rather than prose, so "the Port is
blocked on the Decoder answering X" becomes queryable and closes itself.

PULL REQUESTS, the human's proposal, adopted -- and a bigger improvement than it
looks. Today's long-lived auto/* branches have drifted 280 and 373 commits apart,
which is unreviewable by construction. One PR per item makes the human gate
NATIVE rather than a label convention, binds the change to its item, and enforces
the sizing rule: an item too big to review in one sitting was too big to be an
item.

🔴 Agents must not merge their own PRs, and pull_request_write includes merge --
so this goes in BRANCH PROTECTION on main, not in a document asking them not to.
Same principle that fixed the build-jobs cap: policy where the agent cannot reach
it.

WIKI -- the human suggested it for RE findings, and that half is declined with
reasons. A finding's value is that it sits beside its evidence, versioned with
the code that consumes it; the wiki is a separate git repo, so a decode
correction and the exporter change depending on it could never be one reviewable
PR. And wiki edits bypass review: the REFUTED.md reclassification changed the
file both agents read to decide what not to try, and as a wiki edit it would have
been an unreviewed mutation of shared ground truth. The wiki takes human-facing
orientation instead -- runbook, navigation, container notes, and a landing page,
which closes the real gap that there is no view of what is happening except
container logs.

FILES: three needs, three homes. Agent-to-agent transient stays in /exchange.
Evidence a HUMAN must look at attaches to the issue it belongs to -- it travels
with the item and cannot be orphaned from the claim. Evidence a finding cites
stays in git. Note the MCP exposes attachment_read only; upload needs a direct
REST call.

tools/gitea-setup creates the labels and bundles, idempotently, with --dry-run.
Blocked on a token with write:issue -- the push credential is write:repository
and every issue endpoint refuses it, checked rather than assumed.
2026-09-04 15:46:57 +02:00