Skip to content

Operator Task Issue Creator

Issue2104, plan C10a. The code is the port IOperatorTaskIssueCreator, the pure OperatorTaskIssueCreation (tests) and the adapter AppTokenGitHubOperatorTaskIssueCreator (fake-GitHub tests).

Status: live, behind the task key. The adapter is a scoped service. The confirm route (C10b) calls CreateAsync once, and only after a Committed TryBeginAttemptAsync (task store). The status route calls ListSinceAsync to reconcile an unknown outcome.

Token. The adapter uses the dev-e-bot App installation that DispatchTenantPolicy.ResolveTokenInstallationIdOrDefault maps for the server-derived repo tenant. invotek uses the default install. The adapter never calls TenantGitHubToken.ResolveAsync, which falls back to the GITHUB_TOKEN PAT (plan D11). If the tenant lookup or the mint fails, no GitHub call is made: the create is Refused (no_app_token) and the list is null.

CreateAsync(repoTenant, repo, title, body)

Some requests are refused before any token or call: a repo other than exactly dashecorp/dashe-website (repo_not_allowed), a blank tenant (no_tenant), or a blank title or body (empty_issue).

The call is one POST /repos/dashecorp/dashe-website/issues with exactly {title, body} and no labels, following SelfImprovementService.FileNewIssueAsync. The mint and the POST each have their own 8 s bound. The method takes no caller token, so a disconnected Worker cannot cancel a POST that GitHub may already have received. It never retries.

GitHub answer Result (reason)
201 with a positive number Created(number, https://github.com/{repo}/issues/{number}). The URL is built, not read.
4xx Refused (http_<status>): GitHub created nothing.
201 without a readable number, or any other status Ambiguous (unreadable_created or http_<status>)
Timeout, or transport fault Ambiguous (timeout or transport)

With LOCATION set and the repo's tenant fenced at this location, the POST never reaches GitHub: LocationFenceGitHubHandler answers a synthetic 503 (X-Rig-Location-Fenced), so the result is Ambiguous (http_503) although nothing was created. See multi-location fencing (rig-conductor#2561).

ListSinceAsync(repoTenant, repo, since)

With the same checks and token, the adapter reads one page: GET /repos/dashecorp/dashe-website/issues?state=all&sort=created&direction=desc&per_page=100&since=<UTC second>. GitHub's since filters on updated_at, so the adapter keeps only items with created_at >= since, and drops pull requests. The status route (C10b) reconciles only when exactly one issue that the App filed (user.login is dev-e-bot[bot], OperatorTaskIssueCreation.AppAuthor) carries the exact requestId and attemptId marker. An issue by any other author is ignored, and an App issue that names the requestId with a damaged, duplicated or other-attempt marker makes the answer ambiguous (nothing is bound). Issue search is never used, and the body is data, not evidence.

The result is null, never an empty list, in these cases: - a non-2xx answer, or a body that is not an array; - an item without a readable created_at or number; - a fault or a timeout; - a full page whose oldest item is still inside the window, because more matches may be on the next page.