bug: hire-an-agent does not add the new agent as collaborator on the project repo #856

Closed
opened 2026-04-16 10:29:50 +00:00 by dev-bot · 0 comments
Collaborator

Problem

disinto hire-an-agent <name> <role> creates the Forgejo user, token, password, .profile repo, and project TOML entry — but does not add the new user as a collaborator on the project repo (e.g. disinto-admin/disinto). Without write collaborator status, the agent's issue_claim PATCH request returns 403 Forbidden from Forgejo.

lib/issue-lifecycle.sh:issue_claim() swallows the 403 because the PATCH uses curl -sf ... >/dev/null 2>&1 || return 1 with silenced stderr, so no diagnostic surfaces. Control falls through to the post-PATCH verify added in #830, which reads the assignee as still empty and logs claim lost to <none> — skipping. The caller (dev-agent.sh) emits the generic:

SKIP: failed to claim issue #<N> (already assigned to another agent)

which is misleading — nothing else is assigned; the agent simply has no write permission.

End result: a freshly hired agent container polls forever, launches dev-agent every cycle, each dev-agent fails to claim, exits, and no work ever gets done. Silent zombie.

Repro

  1. disinto hire-an-agent dev-qwen2 dev --local-model <url> --model <name>
  2. Bring up the agent container.
  3. Watch /home/agent/data/logs/dev/dev-agent.log — every cycle logs SKIP: failed to claim issue #<N> (already assigned to another agent).
  4. Check Forgejo: the target issue has no assignee; agent has no write collaborator status on the repo.
  5. Manual test confirms 403:
curl -sv -X PATCH -H "Authorization: token $FORGE_TOKEN_DEV_QWEN2" \
  "http://forgejo:3000/api/v1/repos/disinto-admin/disinto/issues/845" \
  -d '{"assignees":["dev-qwen2"]}'
# => 403 Forbidden

Fix

Two complementary changes:

1. lib/hire-agent.sh — add collaborator step (primary fix)

After the user + token are created, add the new agent as a write collaborator on the project repo(s). Mirror the pattern already used for the other bot users in lib/forge-setup.sh (which is how dev-qwen, dev-bot, review-bot, etc. got collab status originally).

curl -sf -X PUT -H "Authorization: token ${admin_token}" -H "Content-Type: application/json" \
  "${forge_url}/api/v1/repos/${FORGE_REPO}/collaborators/${agent_name}" \
  -d '{"permission":"write"}'

Optionally extend to ops repo for roles that need it (planner/architect/vault). For plain dev role, project repo is sufficient.

2. lib/issue-lifecycle.sh:issue_claim() — surface the 403 (defense in depth)

The silent || return 1 on PATCH hides the root cause. Capture the HTTP status and log it on failure:

local http_code
http_code=$(curl -s -o /dev/null -w '%{http_code}' -X PATCH \
  -H "Authorization: token ${FORGE_TOKEN}" \
  -H "Content-Type: application/json" \
  "${FORGE_API}/issues/${issue}" \
  -d "{\"assignees\":[\"${me}\"]}")
if [ "$http_code" != "201" ] && [ "$http_code" != "200" ]; then
  _ilc_log "issue #${issue} PATCH assignee failed: HTTP ${http_code}"
  return 1
fi

This would have made the collaborator miss diagnosable in seconds instead of minutes.

Acceptance

  • After disinto hire-an-agent X dev, X appears in GET /repos/<project>/collaborators with write permission
  • A newly hired agent's dev-agent successfully claims its first ready issue on the first cycle (no more claim lost to <none> spam)
  • issue_claim logs a distinct, specific message on HTTP error (403/404/5xx) rather than the generic claim lost message
  • Re-running hire-an-agent for an existing agent is idempotent (re-adding an existing collaborator returns 204 without error)

Affected files

  • lib/hire-agent.sh — add collaborator PUT after user + token creation
  • lib/issue-lifecycle.shissue_claim() surface PATCH HTTP errors

Context

Caught today while bringing up dev-qwen2 as a second parallel llama dev agent. Container polled for ~6 minutes, launching a new dev-agent every iteration, each failing silently. Unblocked by manually running:

curl -X PUT -H "Authorization: token ${FORGE_ADMIN_TOKEN}" \
  "http://forgejo:3000/api/v1/repos/disinto-admin/disinto/collaborators/dev-qwen2" \
  -d '{"permission":"write"}'

Next claim cycle succeeded.

Non-goals

  • Ops repo collaborator (only needed for roles that push to ops; dev role doesn't)
  • Branch-protection bypass for bot users (separate concern)
## Problem `disinto hire-an-agent <name> <role>` creates the Forgejo user, token, password, `.profile` repo, and project TOML entry — but does **not** add the new user as a collaborator on the project repo (e.g. `disinto-admin/disinto`). Without write collaborator status, the agent's `issue_claim` PATCH request returns `403 Forbidden` from Forgejo. `lib/issue-lifecycle.sh:issue_claim()` swallows the 403 because the PATCH uses `curl -sf ... >/dev/null 2>&1 || return 1` with silenced stderr, so no diagnostic surfaces. Control falls through to the post-PATCH verify added in #830, which reads the assignee as still empty and logs `claim lost to <none> — skipping`. The caller (`dev-agent.sh`) emits the generic: ``` SKIP: failed to claim issue #<N> (already assigned to another agent) ``` which is misleading — nothing else is assigned; the agent simply has no write permission. End result: a freshly hired agent container polls forever, launches `dev-agent` every cycle, each dev-agent fails to claim, exits, and no work ever gets done. Silent zombie. ## Repro 1. `disinto hire-an-agent dev-qwen2 dev --local-model <url> --model <name>` 2. Bring up the agent container. 3. Watch `/home/agent/data/logs/dev/dev-agent.log` — every cycle logs `SKIP: failed to claim issue #<N> (already assigned to another agent)`. 4. Check Forgejo: the target issue has no assignee; agent has no write collaborator status on the repo. 5. Manual test confirms 403: ```bash curl -sv -X PATCH -H "Authorization: token $FORGE_TOKEN_DEV_QWEN2" \ "http://forgejo:3000/api/v1/repos/disinto-admin/disinto/issues/845" \ -d '{"assignees":["dev-qwen2"]}' # => 403 Forbidden ``` ## Fix Two complementary changes: ### 1. `lib/hire-agent.sh` — add collaborator step (primary fix) After the user + token are created, add the new agent as a write collaborator on the project repo(s). Mirror the pattern already used for the other bot users in `lib/forge-setup.sh` (which is how `dev-qwen`, `dev-bot`, `review-bot`, etc. got collab status originally). ```bash curl -sf -X PUT -H "Authorization: token ${admin_token}" -H "Content-Type: application/json" \ "${forge_url}/api/v1/repos/${FORGE_REPO}/collaborators/${agent_name}" \ -d '{"permission":"write"}' ``` Optionally extend to ops repo for roles that need it (planner/architect/vault). For plain `dev` role, project repo is sufficient. ### 2. `lib/issue-lifecycle.sh:issue_claim()` — surface the 403 (defense in depth) The silent `|| return 1` on PATCH hides the root cause. Capture the HTTP status and log it on failure: ```bash local http_code http_code=$(curl -s -o /dev/null -w '%{http_code}' -X PATCH \ -H "Authorization: token ${FORGE_TOKEN}" \ -H "Content-Type: application/json" \ "${FORGE_API}/issues/${issue}" \ -d "{\"assignees\":[\"${me}\"]}") if [ "$http_code" != "201" ] && [ "$http_code" != "200" ]; then _ilc_log "issue #${issue} PATCH assignee failed: HTTP ${http_code}" return 1 fi ``` This would have made the collaborator miss diagnosable in seconds instead of minutes. ## Acceptance - [ ] After `disinto hire-an-agent X dev`, `X` appears in `GET /repos/<project>/collaborators` with `write` permission - [ ] A newly hired agent's `dev-agent` successfully claims its first ready issue on the first cycle (no more `claim lost to <none>` spam) - [ ] `issue_claim` logs a distinct, specific message on HTTP error (403/404/5xx) rather than the generic `claim lost` message - [ ] Re-running `hire-an-agent` for an existing agent is idempotent (re-adding an existing collaborator returns 204 without error) ## Affected files - `lib/hire-agent.sh` — add collaborator PUT after user + token creation - `lib/issue-lifecycle.sh` — `issue_claim()` surface PATCH HTTP errors ## Context Caught today while bringing up `dev-qwen2` as a second parallel llama dev agent. Container polled for ~6 minutes, launching a new dev-agent every iteration, each failing silently. Unblocked by manually running: ```bash curl -X PUT -H "Authorization: token ${FORGE_ADMIN_TOKEN}" \ "http://forgejo:3000/api/v1/repos/disinto-admin/disinto/collaborators/dev-qwen2" \ -d '{"permission":"write"}' ``` Next claim cycle succeeded. ## Non-goals - Ops repo collaborator (only needed for roles that push to ops; dev role doesn't) - Branch-protection bypass for bot users (separate concern)
dev-bot added the
backlog
priority
labels 2026-04-16 10:29:50 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:29:57 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:29:57 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:29:58 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:29:58 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:31:01 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:31:02 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:31:03 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:31:03 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:32:05 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:32:05 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:32:06 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:32:06 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:33:11 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:33:11 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:33:13 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:33:13 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:34:15 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:34:15 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:34:17 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:34:17 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:35:20 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:35:20 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:35:21 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:35:21 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:36:25 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:36:25 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:36:26 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:36:27 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:37:30 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:37:30 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:37:32 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:37:32 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:39:39 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:39:39 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:39:40 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:39:41 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:40:43 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:40:43 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:40:44 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:40:44 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:41:47 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:41:47 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:41:48 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:41:48 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:42:51 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:42:51 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:42:52 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:42:52 +00:00
dev-qwen2 self-assigned this 2026-04-16 10:43:54 +00:00
dev-qwen2 added
in-progress
and removed
backlog
labels 2026-04-16 10:43:55 +00:00
dev-qwen2 removed their assignment 2026-04-16 10:43:56 +00:00
dev-qwen2 added
backlog
and removed
in-progress
labels 2026-04-16 10:43:56 +00:00
dev-bot self-assigned this 2026-04-16 10:44:26 +00:00
dev-bot added
in-progress
and removed
backlog
labels 2026-04-16 10:44:26 +00:00
dev-bot was unassigned by dev-qwen2 2026-04-16 11:01:06 +00:00
dev-qwen2 removed the
in-progress
label 2026-04-16 11:01:06 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: disinto-admin/disinto#856
No description provided.