Repository navigation
Add janitor's HttpApi foundation with the sync group - #130
Conversation
Tests only: they fail against the current routes until the sync group, the method-gated SameOrigin middleware and the domain errors land.
JanitorApi in @janitor/domain/Api carries the sync group: getSummary,
requestAll and requestRepository, with PUT {repo}/sync dropped. The
cluster serves it through HttpApiBuilder beside the raw routes, behind
Access, Membership and a method-gated SameOrigin check, and the web app
calls it through a derived ApiClient.
requestRepository now refuses with RepositoryNotFound, RepositoryBlocked
or SynchronizationDisabled from the domain instead of prose, and missing
Access keys or teammate admission failures die as an empty 500.
tim-smart
left a comment
There was a problem hiding this comment.
Reviewed 9870601d66165ecde9f03f79681003ecee800ec5 against EFF-2516. No actionable correctness or simplicity findings. Verdict: passes review. This is a comment review because the authenticated account is also the PR author.
The builder is outside the raw-route prefix layer, with Access → Membership → SameOrigin middleware provided to the builder rather than the group. The real-route tests cover reachability, missing assertions, cross-site writes versus reads, and malformed path parameters. They guard both known wiring mistakes.
The documented web deviations are acceptable: repository IDs are validated at route parsing and again before typed-client calls despite the route type remaining string; the connections page reuses its existing reason-specific copy. The final test-only commit supplies the missing provider and request services without weakening inferred context types.
Independent validation through Nix:
pnpm exec vp check: 0 errors, 323 warnings.pnpm exec vp test: 129 files, 812 tests passed.
The PR description still reports the superseded type error and test timeout; please update its validation section to reflect the final commit.
SyncStatus dies on infrastructure failures everywhere, so SyncStatusError, its wrap helper and the handlers' orDie go. The sync handlers read the service with SyncStatus.use. The web's two sync commands share one refusal copy function and assert the already-validated id instead of re-decoding it. The test client takes its group names explicitly, and the routes test drops the duplicated RuntimeContext middleware.
tim-smart
left a comment
There was a problem hiding this comment.
Reviewed the simplification delta 9870601..94d3a13. Verdict: passes review; no actionable findings. This is a comment review because the authenticated GitHub account is also the PR author.
Standards
No correctness or simplicity regressions found. Dispatch still follows successful sync requests and remains best-effort. Moving infrastructure failures into SyncStatus removes redundant wrapping without changing the HTTP failure contract. The shared refusal copy avoids duplicate error mapping.
Spec
The delta preserves EFF-2516 acceptance criteria: typed repository refusals, infrastructure defects, typed-client calls, and real-route wiring coverage. Using .make is safe at the current call sites because route parsing validates the sync button's repository ID and the retry action uses server-issued inventory IDs. Removing the duplicate runtime-context middleware retains request-context provision.
Independent validation at 94d3a13 through Nix:
pnpm exec vp check: 0 errors, 323 warnings.pnpm exec vp test: 129 files, 812 tests passed, including the affected cluster and web tests.
All four GitHub CI checks are also green at this head. No code changes made.
Closes EFF-2516
Adds
JanitorApiand moves the sync routes onto it as the first group.Domain
@janitor/domain/ApidefinesJanitorApiwith the/api/v1prefix.Api/Sync.tshasgetSummary,requestAllandrequestRepository.PUT /repositories/:id/syncis gone.RepositoryNotFound(404) moves fromLabeling/Configuration.tstoGitHub/Repository.tswith the same tag, next to the newSynchronizationDisabled(409).Cluster
Ingress/Sync.tsis now oneHttpApiBuilder.group(JanitorApi, "sync", …)layer.SyncStatus.requestRepositoryreadsrepository_block_reason(). It fails withRepositoryNotFound,RepositoryBlocked { reason }orSynchronizationDisabled. Infrastructure failures die across the whole service, soSyncStatusErroris gone.Routes.tsmergesHttpApiBuilder.layer(JanitorApi)beside the raw routes and outsideApiRouterLayer. The middleware is provided to the builder layer, as onecombinechain: Access, then Membership, then SameOrigin.ApiRouterLayernow serves only Live, Readiness and the webhook.Middleware.tsownsisSameOriginand both SameOrigin wrappers.SameOriginWritesMiddlewarelets GET and HEAD through and checks everything else; Live keeps the unconditional check.AccessKeysUnavailable, a missingTeammatesand a failedadmitnow die, so the response is an empty 500 instead of 401 or 503.Worker.tsprovidesAccessJwt.layerFrom(...)andFileSystem.layerNoop({})tomakeRoutesLayer.Web
lib/api-client.tsaddsApiClient, built once inentry.tsonFetchHttpClient.layerand added toAppServices.api.sync.*and share onesyncRefusalCopyfor the three refusal tags. The duplicatedSyncSummaryschema is gone.routes.tsdecodes the repository segment withGitHubRepositoryDatabaseId, so a path segment that isn't an id now matches no route. The route still typesrepositoryIdas a string: typing it with the brand would spread into the wire models (ConnectionCandidate,ConfigurationView,labeling-wire) that later group PRs move. The commands assert the brand withGitHubRepositoryDatabaseId.makebefore calling the client.Validation
At
94d3a13:nix develop -c pnpm exec vp check: 0 errors (323 existing warnings).nix develop -c pnpm exec vp test: 1039 of 1043 pass under full-suite load; the four failures are 5-second timeouts inPullRequestReview/{Publish,ReviewAgent,RunEntity}.test.ts, which pass 58/58 when run on their own. The fourRoutes.test.tswiring cases pass.Routes.test.tschecks the two wiring mistakes the decisions warn about: with the middleware provided to the group layer, the 401 and 403 cases fail; with the builder insideApiRouterLayer, all four fail.