The server side of #228/relayfile-cloud#159 shipped; Factory has no caller
relayfile-cloud#159 ("Mount-native app-actor GitHub read: let a caller request a read as the App installation") closed COMPLETED on 2026-09-02, shipped in relayfile-cloud PR #160. It adds POST /v1/workspaces/:workspaceId/github/repos/:owner/:repo/issues/:number/read with body {"actor":"app"}, requires fs:read scope, and returns the exact three-outcome shape Factory already knows how to consume:
type GithubIssueLookup =
| { outcome: "found"; issue: GithubConnectionIssue }
| { outcome: "not-found" }
| { outcome: "indeterminate"; reason: string }
(relayfile-cloud packages/relayfile/src/writeback/providers/github.ts:70-73)
Factory's consumer side was deliberately left unwired pending this — see src/writeback/github.ts:100-103:
Provider-authoritative reads remain intentionally absent until the mount exposes an app-actor read contract; GithubWriteback models those as optional.
That contract now exists. Factory doesn't call it. src/mount/relayfile-cloud-mount-client.ts:381 still unconditionally constructs the unauthenticated reader:
this.githubRead = new GithubApiIssueRead()
GithubApiIssueRead (src/mount/github-api-issue-read.ts) sends no Authorization header by design (that invariant is correct and stays — see #228). Its consumer, #readGithubIssue at src/orchestrator/factory.ts:7554-7584, already branches on lookup.outcome in the exact shape the new route returns (:7584-7600). So every private-repo issue Factory tries to read through the API fallback still resolves indeterminate today, even though the mount-native authenticated path that would resolve it found/not-found has been live for a day.
This is a merged capability with no caller — inert until wired.
Fix shape (matches the shape proposed and left un-actioned on #228, now unblocked)
- Add a
GithubConnectionRead implementation that calls the new mount route with actor: "app" instead of GitHub directly — same call pattern relayfile-github-connection-write.ts already uses for author: 'app' writes (mount-native, no local credential).
- Wire
relayfile-cloud-mount-client.ts:381 to use it. Whether the existing unauthenticated GithubApiIssueRead stays as a fallback for unconfigured/non-cloud deployments, or is retired outright now that the mount fulfills reads server-side unconditionally, is an open call for whoever picks this up — the mount route requires no deployment-side credential, so there may be no case left that needs the unauthenticated fallback.
- Preserve the three outcomes exactly — do not collapse
indeterminate into found/not-found. factory.ts:7590-7600's handling of indeterminate (counts githubIssueUnverifiable, does not promote to found) must keep working against the new reader's indeterminate responses (5xx/timeout at the mount, or a repo the App genuinely can't see).
Definition of done
- Unit test: a private-repo issue lookup resolves
outcome: 'found' through the new App-actor route (mock the mount response).
- Must-not-fire: a mount
not-found still resolves not-found; a mount indeterminate still resolves indeterminate and still increments githubIssueUnverifiable — do not regress #228's must-not-fire test.
- Negative-auth arm not required here (the mount owns credential handling now); a fetch/mount-error path must still map to
indeterminate, not not-found or a thrown error that skips the issue silently.
- Confirm against a real private repo issue if a live credentialed workspace is reachable from the test/dev environment; otherwise a stubbed mount response is sufficient and should be flagged as such.
Related: #228 (design note, Axis B ruling), relayfile-cloud#159 (server side, closed).
The server side of #228/relayfile-cloud#159 shipped; Factory has no caller
relayfile-cloud#159("Mount-native app-actor GitHub read: let a caller request a read as the App installation") closed COMPLETED on 2026-09-02, shipped inrelayfile-cloudPR #160. It addsPOST /v1/workspaces/:workspaceId/github/repos/:owner/:repo/issues/:number/readwith body{"actor":"app"}, requiresfs:readscope, and returns the exact three-outcome shape Factory already knows how to consume:(
relayfile-cloud packages/relayfile/src/writeback/providers/github.ts:70-73)Factory's consumer side was deliberately left unwired pending this — see
src/writeback/github.ts:100-103:That contract now exists. Factory doesn't call it.
src/mount/relayfile-cloud-mount-client.ts:381still unconditionally constructs the unauthenticated reader:GithubApiIssueRead(src/mount/github-api-issue-read.ts) sends noAuthorizationheader by design (that invariant is correct and stays — see #228). Its consumer,#readGithubIssueatsrc/orchestrator/factory.ts:7554-7584, already branches onlookup.outcomein the exact shape the new route returns (:7584-7600). So every private-repo issue Factory tries to read through the API fallback still resolvesindeterminatetoday, even though the mount-native authenticated path that would resolve itfound/not-foundhas been live for a day.This is a merged capability with no caller — inert until wired.
Fix shape (matches the shape proposed and left un-actioned on #228, now unblocked)
GithubConnectionReadimplementation that calls the new mount route withactor: "app"instead of GitHub directly — same call patternrelayfile-github-connection-write.tsalready uses forauthor: 'app'writes (mount-native, no local credential).relayfile-cloud-mount-client.ts:381to use it. Whether the existing unauthenticatedGithubApiIssueReadstays as a fallback for unconfigured/non-cloud deployments, or is retired outright now that the mount fulfills reads server-side unconditionally, is an open call for whoever picks this up — the mount route requires no deployment-side credential, so there may be no case left that needs the unauthenticated fallback.indeterminateintofound/not-found.factory.ts:7590-7600's handling ofindeterminate(countsgithubIssueUnverifiable, does not promote to found) must keep working against the new reader'sindeterminateresponses (5xx/timeout at the mount, or a repo the App genuinely can't see).Definition of done
outcome: 'found'through the new App-actor route (mock the mount response).not-foundstill resolvesnot-found; a mountindeterminatestill resolvesindeterminateand still incrementsgithubIssueUnverifiable— do not regress#228's must-not-fire test.indeterminate, notnot-foundor a thrown error that skips the issue silently.Related: #228 (design note, Axis B ruling), relayfile-cloud#159 (server side, closed).