docs: fold the page's revisions into the file, and separate wrong from unchecked
The runbook existed as two documents -- a published page and this file -- with
no mechanism keeping them equal, only an intention to remember. Two versions was
the predicted outcome of that, not an accident on top of it. This is the fold,
and the rule that follows it: THIS FILE IS THE SOURCE, the page is derived from
it. When something is urgent enough to push to the page first, it lands here in
the same turn, not "shortly after".
Four things the file did not carry:
* YOUR OWN PUSHES TO main STOP. `enable_push: false` compiles to CanUserPush,
which returns false with no bypass for admins or the owner -- quoted from
the source. Three commits went in by direct push the day this was written,
so the first notice would have been mid-task. Now a check step.
* the token files' MACHINES, which the table had lost.
* do NOT add `write:repository` to the `fabi` token. That scope IS a push
credential. Written down because that advice was given, in chat, by the
author of this file.
* Gitea 1.25.5 confirmed from the desktop too, not just the Pi.
And one thing deliberately NOT folded in: the page said the desktop's outbound
HTTP was blocked, and that is false. `python3 -c 'urllib...'` returns
200 {"version":"1.25.5"} from this box. What is refused here is `curl`, by a
local permission prompt -- which I read as a network constraint and then
published as one. The Phase 3 locations stand; the reason given for them did not.
The "not verified" section now separates WRONG from UNCHECKED. Four entries are
wrong -- requiring an approval does not close the gate, the check could not have
caught that, the token scope, the reachability -- and the pattern in all four is
identical: a property inferred from something ADJACENT to it (protection from a
settings page, reachability from a DNS record) instead of tested directly. That
is the frozen-splash failure, committed in the document about avoiding it. The
first two were caught by the other agent, which is the argument for the review
gate this file exists to build.
This commit is contained in:
@@ -127,11 +127,32 @@ these, not a substitute: an agent still has a browser-shaped API token.
|
||||
> button**, now that an approval exists. This is the step that tests the
|
||||
> merge whitelist rather than the absence of an approval — without it, steps
|
||||
> 2 and 3 pass on an instance where the agents can merge each other's work.
|
||||
> 5. Close the PR, delete the branch.
|
||||
> 5. As **yourself**, try `git push origin main` with a throwaway commit. It
|
||||
> should be **refused** — see below.
|
||||
> 6. Close the PR, delete the branch, drop the commit.
|
||||
>
|
||||
> If step 2 or step 4 offers a Merge button, stop — the rest of this runbook
|
||||
> assumes neither does.
|
||||
|
||||
### ⚠️ Your own pushes to `main` stop too
|
||||
|
||||
Not a side effect — the rule working. `enable_push: false` compiles to
|
||||
`CanUserPush`, which in Gitea's `models/git/protected_branch.go` returns false
|
||||
with **no bypass for repository admins or the owner**:
|
||||
|
||||
```go
|
||||
if !protectBranch.CanPush {
|
||||
return false
|
||||
}
|
||||
```
|
||||
|
||||
Three commits reached `main` by direct push on the day this was written, so the
|
||||
first time you notice will be the first time you reach for it. From Phase 2 on,
|
||||
**human changes go through pull requests like everything else** — and merging
|
||||
them is what the admin override above is for. `--verify` asserts this state
|
||||
rather than tolerating it: a verifier that excused your push would be excusing
|
||||
the gate.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 · Tokens 👤
|
||||
@@ -139,11 +160,21 @@ these, not a substitute: an agent still has a browser-shaped API token.
|
||||
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` |
|
||||
| whose | scopes | goes in | on which machine |
|
||||
|---|---|---|---|
|
||||
| **you** (`fabi`) | `write:issue`, `read:repository` | `~/.sylph-gitea-api-token` | **the Pi** |
|
||||
| `sylph-decoder` | `write:repository`, `write:issue`, `write:notification`, `read:user` | `~/.sylph-gitea-token-decoder` | the agent box |
|
||||
| `sylph-port` | same four | `~/.sylph-gitea-token-port` | the agent box |
|
||||
|
||||
📌 **Three machines, and the split is by tooling, not by capability.** Gitea runs
|
||||
on the Pi, published through a VPS — so `git.mc02.dev` resolves to a hosted
|
||||
address and a DNS lookup tells you nothing about the origin. The agent
|
||||
containers run on the x86_64 desktop, which reaches the Gitea API perfectly well
|
||||
(`GET /api/v1/version` → `200 {"version":"1.25.5"}`, run from there).
|
||||
|
||||
The `fabi` token lives on the Pi because that is where `tools/gitea-setup` runs,
|
||||
and that is where the session driving Phases 4 and 6 sits. It is **not** a
|
||||
reachability constraint, and an earlier draft that said so was wrong.
|
||||
|
||||
```bash
|
||||
printf '%s\n' '<token>' > ~/.sylph-gitea-api-token && chmod 600 ~/.sylph-gitea-api-token
|
||||
@@ -157,6 +188,14 @@ containers, exactly like `~/.sylph-claude-token`.
|
||||
`required=[read:issue], token scope=write:repository`. It stays as it is; these
|
||||
are additional.
|
||||
|
||||
🔴 **Do not add `write:repository` to the `fabi` token**, even though Phase 2's
|
||||
API path might look as though it needs it. **A `write:repository` token *is* a
|
||||
push credential** — that is the scope git checks for receive-pack — so adding it
|
||||
would give the Pi push rights over `main`, in order to avoid giving the Pi push
|
||||
rights. `gitea-protect` sidesteps it entirely by running on the agent box
|
||||
against the credential already there. This warning exists because that advice
|
||||
was given, in chat, by the same author as this file.
|
||||
|
||||
> **Check:** `tools/gitea-setup --dry-run` prints "would create …" rather than a
|
||||
> scope error.
|
||||
|
||||
@@ -320,11 +359,38 @@ does not:
|
||||
which is what the approvals whitelist is for.
|
||||
* **Gitea's Projects API**, which is why Phase 4 creates no board.
|
||||
|
||||
Two things on this list have since been settled rather than assumed:
|
||||
Settled since, rather than assumed:
|
||||
|
||||
* ~~the `--tools` filter names~~ — read out of the pinned release, and the
|
||||
binary's `--help` run directly. Phase 5 lists the set.
|
||||
* ~~the exact Gitea version~~ — **1.25.5**. `enable_merge_whitelist`,
|
||||
`enable_approvals_whitelist` and `block_admin_merge_override` are all present
|
||||
in this instance's own API schema, so the Phase 2 settings exist under those
|
||||
names on the Branches screen.
|
||||
* ~~the exact Gitea version~~ — **1.25.5**, confirmed independently from *both*
|
||||
machines. `enable_merge_whitelist`, `enable_approvals_whitelist` and
|
||||
`block_admin_merge_override` are all present in this instance's own API
|
||||
schema, so the Phase 2 settings exist under those names on the Branches screen.
|
||||
* ~~which machine can reach what~~ — the desktop reaches the Gitea API fine.
|
||||
The token split in Phase 3 is about which session runs which script, and an
|
||||
earlier draft that justified it as a network constraint was wrong.
|
||||
|
||||
### Wrong, not merely unverified
|
||||
|
||||
Kept separate, because "I had not checked" and "I asserted the opposite" are
|
||||
different failures and only the second is worth a heading:
|
||||
|
||||
* **that requiring an approval closes the gate.** It does not. Merging ignores
|
||||
the push whitelist entirely, and any Write collaborator is an official
|
||||
reviewer — so the first version of Phase 2 would have let the two agents
|
||||
approve each other and merge. Both whitelists exist because of it.
|
||||
* **that the check could catch that.** It could not: with the approval
|
||||
requirement unmet, Gitea offers *nobody* a merge button, so the original
|
||||
steps 1–3 pass on a completely unprotected instance. Step 4 is the test.
|
||||
* **that the `fabi` token should gain `write:repository`.** That scope is a push
|
||||
credential.
|
||||
* **that the desktop could not reach Gitea.** It can; `curl` was being refused
|
||||
by a local permission prompt, which is not the same thing and was read as if
|
||||
it were.
|
||||
|
||||
The first two were caught by the other agent. The pattern in all four is one
|
||||
thing: **a property was inferred from something adjacent to it** — protection
|
||||
from a settings page, reachability from a DNS record — instead of being tested
|
||||
directly. That is the same failure the port's frozen-splash instruments made,
|
||||
in a document about avoiding it.
|
||||
|
||||
Reference in New Issue
Block a user