Skip to content

Reviewer agent names collide across repos: ar-<number>-review is not repo-qualified (implementer is) #82

Description

@khaliqgant

Summary

Reviewer agent names are derived from the issue number alone, with no repo qualifier — so the same issue number in two different repos produces the same agent name, and the second dispatch collides with a name the broker still holds. The implementer does not have this problem: it is already repo-scoped.

This makes factory effectively single-repo per issue-number-space, which contradicts the multi-repo routing the config is built around (repos.names[], byLabel, byProject, keywordRules, repos.default).

Observed

Dispatching hoopsheet#26 after relayed#26 had been dispatched previously (2026-07-15):

$ factory dispatch /github/repos/AgentWorkforce__hoopsheet/issues/by-id/26.json
[factory-sdk] marked spawned agent terminal {"name":"ar-26-review","reason":"resume-already-exists"}
[factory] resume skipped: broker still holds agent name (relay#1116); not retrying {"issue":"26","name":"ar-26-review"}
[factory-sdk] suppressed duplicate agent exit {"count":1,"name":"ar-26-review"}
[factory] error {}
[factory] task injection target not registered yet; retrying {"to":"ar-26-review","attempt":1,"error":"Timed out waiting for delivery_injected for http_…"}
[factory] task injection target not registered yet; retrying {"to":"ar-26-review","attempt":2,…}
[factory] task injection target not registered yet; retrying {"to":"ar-26-review","attempt":3,…}

The dispatch wedges: the reviewer never registers, and factory retries task injection into an agent that will never exist.

Root cause

src/triage/heuristic.ts:376:

function agentBaseName(issue: LinearIssue): string {
  const number = issue.key.match(/\d+/)?.[0] ?? sanitizeSlug(issue.key)
  return `ar-${number}`          // issue number only — no repo
}

For GitHub issues issue.key is the bare number, so hoopsheet#26 and relayed#26 both yield ar-26. The implementer name gets a repo suffix (slugFromRepo, heuristic.ts:381) → ar-26-impl-hoopsheet. The reviewer does not → bare ar-26-review. Hence implementers never collide and reviewers always do.

(Duplicate agentBaseName also exists at src/orchestrator/factory.ts:5697 — worth checking they stay in sync, or de-duplicating.)

Scale of the problem in one workspace

Every reviewer from this workspace's history is squatting on the global name space:

ar-12-impl-relayed | ar-12-review     ← implementer repo-scoped, reviewer NOT
ar-13-impl-relayed | ar-13-review
ar-24-impl-relayed | ar-24-review
ar-25-impl-relayed | ar-25-review
…

Poisoned numbers currently registered (all offline/unknown, all still holding the name):
7, 9, 12, 13, 14, 15, 16, 19, 21, 23, 24, 25, 26, 27, 28, 29, 31, 33, 41, 43, 45

Consequence for a second repo in the same workspace: 14 of hoopsheet's 22 factory-ready issues cannot be dispatched (12, 13, 14, 15, 23, 24, 25, 26, 27, 28, 29, 31, 33, 41). Only 11, 22, 30, 34, 35, 36, 37, 38 are usable — purely by the accident of which numbers relayed happened to use.

Fix

Repo-qualify the reviewer exactly as the implementer already is:

ar-26-impl-hoopsheet     (today)
ar-26-review-hoopsheet   (proposed)

Points to consider:

Workaround

Release the held name before re-dispatching:

agent-relay node agent release ar-26-review     # or: factory reap-orphans

Impact

Multi-repo is a headline capability (repos.byLabel / byProject / keywordRules / repos.default all exist to route across repos), but the reviewer name space is global and unqualified — so the second repo onboarded into a workspace silently loses most of its issue numbers, with a failure mode (task injection target not registered yet, retried forever) that gives no hint the cause is a name collision with a different repo.

Related: relay#1116 (broker holds agent name), #59 / #67 / #69 (already exists respawn handling), #81 (dispatch resolver ergonomics).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions