diff --git a/.gitea/workflows/agent.yml b/.gitea/workflows/agent.yml index 1bbf50c..e373716 100644 --- a/.gitea/workflows/agent.yml +++ b/.gitea/workflows/agent.yml @@ -169,9 +169,10 @@ jobs: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # SELF_TOKEN = the RUNNING agent's OWN token (TOKEN_PM for @pm, TOKEN_OPS for @ops, …). # Only this agent's token is placed in its process env, so no agent can act as another. - # Powers the gitea-api / gitea-admin skills — each agent calls Gitea as itself. Falls back - # to AGENT_TOKEN in repos that haven't been migrated to per-agent tokens (e.g. homelab). - SELF_TOKEN: ${{ steps.prep.outputs.name == 'pm' && secrets.TOKEN_PM || steps.prep.outputs.name == 'junior' && secrets.TOKEN_JUNIOR || steps.prep.outputs.name == 'senior' && secrets.TOKEN_SENIOR || steps.prep.outputs.name == 'lead' && secrets.TOKEN_LEAD || steps.prep.outputs.name == 'qa' && secrets.TOKEN_QA || steps.prep.outputs.name == 'ops' && secrets.TOKEN_OPS || secrets.AGENT_TOKEN }} + # Powers the gitea-api / gitea-admin skills — each agent calls Gitea as itself. Every + # consuming repo now carries the per-agent TOKEN_* secrets (org-level for gitea/*, user-level + # for ffaerber/*), so there is no shared-token fallback. + SELF_TOKEN: ${{ steps.prep.outputs.name == 'pm' && secrets.TOKEN_PM || steps.prep.outputs.name == 'junior' && secrets.TOKEN_JUNIOR || steps.prep.outputs.name == 'senior' && secrets.TOKEN_SENIOR || steps.prep.outputs.name == 'lead' && secrets.TOKEN_LEAD || steps.prep.outputs.name == 'qa' && secrets.TOKEN_QA || steps.prep.outputs.name == 'ops' && secrets.TOKEN_OPS || '' }} NAME: ${{ steps.prep.outputs.name }} MODEL: ${{ steps.prep.outputs.model }} VISION: ${{ steps.prep.outputs.vision }} @@ -197,9 +198,6 @@ jobs: env: SCRIPTS: ${{ runner.temp }}/agents-scripts GT: ${{ secrets.GITEA_TOKEN }} - # Fallback PAT for triggering comments (delegation/autopilot) + merges in repos without - # per-agent tokens. Where per-agent tokens exist, the agent's own token is used instead. - AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }} TOKEN_PM: ${{ secrets.TOKEN_PM }} TOKEN_SENIOR: ${{ secrets.TOKEN_SENIOR }} TOKEN_JUNIOR: ${{ secrets.TOKEN_JUNIOR }} @@ -226,9 +224,6 @@ jobs: env: SCRIPTS: ${{ runner.temp }}/agents-scripts GT: ${{ secrets.GITEA_TOKEN }} - # Fallback PAT for triggering comments (delegation/autopilot) + merges in repos without - # per-agent tokens. Where per-agent tokens exist, the agent's own token is used instead. - AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }} TOKEN_PM: ${{ secrets.TOKEN_PM }} TOKEN_SENIOR: ${{ secrets.TOKEN_SENIOR }} TOKEN_JUNIOR: ${{ secrets.TOKEN_JUNIOR }} diff --git a/.gitea/workflows/scripts/publish.sh b/.gitea/workflows/scripts/publish.sh index 8020414..bb568e9 100755 --- a/.gitea/workflows/scripts/publish.sh +++ b/.gitea/workflows/scripts/publish.sh @@ -13,10 +13,12 @@ case "$NAME" in esac [ -z "$TOK" ] && TOK="$GT" # Trigger token: comments that must FIRE the next workflow (delegation, autopilot) and PR merges -# cannot use the built-in GITEA_TOKEN (Gitea won't start new runs from it). Use the agent's own PAT -# when it has one; otherwise fall back to AGENT_TOKEN (repos without per-agent tokens, e.g. homelab). +# cannot use the built-in GITEA_TOKEN (Gitea won't start new runs from it) — they need a real PAT. +# Every agent now has its own token, so TTOK is just the agent's token. If an agent somehow has none +# (TOK fell back to the built-in GT), TTOK is left empty so the trigger/merge is skipped rather than +# silently no-op'ing under the built-in token. TTOK="$TOK" -{ [ -z "$TTOK" ] || [ "$TTOK" = "$GT" ]; } && TTOK="${AGENT_TOKEN:-}" +[ "$TTOK" = "$GT" ] && TTOK="" git config user.name "$NAME" git config user.email "$NAME@ffaerber.duckdns.org" API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" diff --git a/README.md b/README.md index aad52b0..9a76d9e 100644 --- a/README.md +++ b/README.md @@ -78,13 +78,14 @@ points `$SCRIPTS` at it. Keep the workflow and its scripts moving together on `m | `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, comments, and (for `@qa` autopilot) merges as itself, and its `gitea-api` skill acts with its own scopes. `TOKEN_QA` needs `write:repository` to merge. | | `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`. | -| `AGENT_TOKEN` | **fallback** — an admin PAT used only where per-agent `TOKEN_*` aren't configured (e.g. an un-migrated consuming repo). Covers the two things the built-in `GITEA_TOKEN` can't do: post the delegation/autopilot comment that *fires the next run*, and merge a PR so the push *triggers downstream deploys*. Where per-agent tokens exist, each agent uses its own instead. | Each agent authenticates as **itself**: the Run-agent step selects that agent's `TOKEN_*` into -`SELF_TOKEN` (never another agent's), falling back to `AGENT_TOKEN` only when its own token is unset. -`GITEA_TOKEN` is auto-provided (used for reads, and as the reply identity only when an agent has no -`TOKEN_*` of its own). Tip: set these once at the **org** level so every repo inherits them via -`secrets: inherit`. +`SELF_TOKEN` (never another agent's), and `publish.sh` uses the same token for the delegation/autopilot +trigger comment and `@qa` merges — 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