Problem
contributor-issue-draft.ts's createGitHubContributorIssue and ruleset.ts's fileUpstreamDriftIssues both write GitHub issues via a flat operator PAT (LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN ?? LOOPOVER_DRIFT_ISSUE_TOKEN ?? GITHUB_PUBLIC_TOKEN) through a raw fetch, bypassing makeInstallationOctokit/createInstallationToken (src/github/app.ts, src/github/client.ts) and the Orb broker (src/orb/broker-client.ts) that every other GitHub write in this codebase (labels, comments, collaborator-permission checks) already goes through. That PAT must itself hold write access to whichever repo is targeted — it does not automatically follow "wherever this selfhoster's App/Orb-installation is installed" the way every other write path does. A selfhoster-facing issue/milestone-planning tool built on this pattern would require every selfhoster to separately mint and manage a write-capable PAT just for this one feature, defeating the point of the Orb broker existing.
Area
src/github/app.ts, src/github/client.ts, src/orb/broker-client.ts, src/services/contributor-issue-draft.ts, src/upstream/ruleset.ts.
Proposal (open questions a design pass needs to answer)
- A shared resolver, given a target repo, should return a write-capable Octokit client by trying (1) the selfhoster's own GitHub App installation token via
makeInstallationOctokit, then (2) the Orb broker path (fetchBrokeredInstallationToken) if no local App key is configured. The flat-PAT fallback should not be offered to the new selfhoster-facing tool at all.
- Decide whether
contributor-issue-draft.ts and ruleset.ts's existing PAT-based writers get migrated onto the same resolver (for consistency) or are left as-is with the divergence documented (they only ever target loopover's own repo, a narrower risk surface than an arbitrary selfhosted repo) — this is a maintainer call, not purely mechanical.
- Confirm the resolver's error behavior when neither an App key nor a broker enrollment is configured: the new tool must fail closed with a clear "not configured" message, never silently fall back to an unscoped token.
Deliverables
Resources
src/github/app.ts (makeInstallationOctokit, createInstallationToken)
src/github/client.ts
src/orb/broker-client.ts (isOrbBrokerMode, fetchBrokeredInstallationToken)
src/services/contributor-issue-draft.ts:555 (createGitHubContributorIssue, the PAT-based pattern being replaced for new callers)
src/upstream/ruleset.ts:325 (fileUpstreamDriftIssues, same PAT pattern)
Boundaries
maintainer-only. This blocks the rest of the epic — the planning MCP tool (sub-issue 2) must not ship until this resolver exists, since it must not offer selfhosters a new PAT-based path.
- Must not weaken the Orb broker's existing "never trust the caller's installation_id, always bind server-side to the registered install" guarantee (
src/orb/broker.ts).
- Not scoped to change how labels/comments/collaborator-checks already resolve their client — those are unaffected; this is specifically about closing the gap for issue/milestone-writing code.
Problem
contributor-issue-draft.ts'screateGitHubContributorIssueandruleset.ts'sfileUpstreamDriftIssuesboth write GitHub issues via a flat operator PAT (LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN ?? LOOPOVER_DRIFT_ISSUE_TOKEN ?? GITHUB_PUBLIC_TOKEN) through a rawfetch, bypassingmakeInstallationOctokit/createInstallationToken(src/github/app.ts,src/github/client.ts) and the Orb broker (src/orb/broker-client.ts) that every other GitHub write in this codebase (labels, comments, collaborator-permission checks) already goes through. That PAT must itself hold write access to whichever repo is targeted — it does not automatically follow "wherever this selfhoster's App/Orb-installation is installed" the way every other write path does. A selfhoster-facing issue/milestone-planning tool built on this pattern would require every selfhoster to separately mint and manage a write-capable PAT just for this one feature, defeating the point of the Orb broker existing.Area
src/github/app.ts,src/github/client.ts,src/orb/broker-client.ts,src/services/contributor-issue-draft.ts,src/upstream/ruleset.ts.Proposal (open questions a design pass needs to answer)
makeInstallationOctokit, then (2) the Orb broker path (fetchBrokeredInstallationToken) if no local App key is configured. The flat-PAT fallback should not be offered to the new selfhoster-facing tool at all.contributor-issue-draft.tsandruleset.ts's existing PAT-based writers get migrated onto the same resolver (for consistency) or are left as-is with the divergence documented (they only ever target loopover's own repo, a narrower risk surface than an arbitrary selfhosted repo) — this is a maintainer call, not purely mechanical.Deliverables
contributor-issue-draft.ts,ruleset.ts) migrate onto it.Resources
src/github/app.ts(makeInstallationOctokit,createInstallationToken)src/github/client.tssrc/orb/broker-client.ts(isOrbBrokerMode,fetchBrokeredInstallationToken)src/services/contributor-issue-draft.ts:555(createGitHubContributorIssue, the PAT-based pattern being replaced for new callers)src/upstream/ruleset.ts:325(fileUpstreamDriftIssues, same PAT pattern)Boundaries
maintainer-only. This blocks the rest of the epic — the planning MCP tool (sub-issue 2) must not ship until this resolver exists, since it must not offer selfhosters a new PAT-based path.src/orb/broker.ts).