MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3564

(motir-core) The E2E seam for the index WRITER path — the run-credential mint and the tarball redirect, on the app server AND the worker

Done
Description

Give the main E2E lane the two seams the index fleet's WRITER path crosses, so a spec can drive system.code-graph-refresh end to end — boot, poll, settle — against the fake orchestrator, with no real container, no Fly token and no network.

Why this is not already there

MOTIR_FLEET_ORCHESTRATOR=fake is set (playwright.config.ts), and indexFleetConfig() returns a fake digest under it, so the CONTAINER half is already stubbed. What is not stubbed is what bootIndexContainer does BEFORE it provisions, and both calls leave the process:

  1. mintCodeGraphRunCredential -> POST {MOTIR_AI_URL}/v1/code-graph/run-credential. MOTIR_AI_URL / MOTIR_AI_SERVICE_TOKEN are set only in playwright.acceptance.config.ts, and NO mock in lib/test-*mock*.ts intercepts that path.
  2. requireRepoTarballUrlResolver(getGitProvider('github')) -> an installation token minted from the App JWT, then GET /repos/{owner}/{name}/tarball/{ref} read for its 302 Location. GITHUB_APP_ID / GITHUB_APP_PRIVATE_KEY are set in NEITHER lane, and lib/test-github-repos-mock.ts intercepts the provisioning paths, not the tarball redirect.

tests/jobs/code-graph-index.test.ts stubs both in-process (stubIndexFleet in tests/helpers/indexFleet.ts). A Playwright server is a separately-spawned process, so an in-process stub cannot reach it — which is exactly the reason every other boundary in this lane has a lib/test-*-mock.ts installed by instrumentation.ts behind a flag.

The shape to build

Follow lib/test-github-repos-mock.ts and lib/test-billing-mock.ts verbatim — same MockAgent, same instrumentation.ts flag registration, same journal. Two intercepts and the env to reach them:

  1. lib/test-code-graph-mock.ts, behind E2E_TEST_CODE_GRAPH=1, intercepting the MOTIR_AI_URL origin's POST /v1/code-graph/run-credential with a well-formed CodeGraphRunCredential (an opaque credential string and an expiresAt), and api.github.com's GET /repos/{owner}/{name}/tarball/{ref} with a 302 carrying a Location — the shape resolveTarballUrl reads, not a body.
  2. The lane's env: MOTIR_AI_URL, MOTIR_AI_SERVICE_TOKEN, GITHUB_APP_ID and a generated GITHUB_APP_PRIVATE_KEY on the app webServer, and — this is the half a reader will miss — on the WORKER process too. tests/e2e/_helpers/job-worker-process.ts spawns a THIRD process that inherits the RUNNER's env, not webServer.env; MOTIR-3498 was filed because EMAIL_PROVIDER was set on the webServer only and every engine-routed send went to the console provider. A supervisor runs IN THE WORKER, so the worker is the process that actually makes both calls.

Scope boundary

ENDS at: a Playwright spec being able to drive runIndexFleetSteps to a succeeded job_run carrying output.repoRef, on the engine, in the main lane.

Adds NO new webServer entry and NO new service — one more instrumentation.ts mock behind one more flag, which is the lane's established shape.

Does NOT write the spec. MOTIR-3487 does, and is blocked on this.

Does NOT touch the acceptance lane, which already sets MOTIR_AI_URL for other reasons.

Does NOT stub the orchestrator — the fake adapter is already selected by the shipped config seam and is not a mock.

Acceptance criteria

  • With the flag on, a system.code-graph-refresh run claimed by the lane's worker reaches settleIndexContainer and writes ONE succeeded job_run per repo carrying one output.repoRef — asserted by a spec-level smoke in this card, so the seam is proven by the thing that will consume it.
  • The run-credential intercept returns a shape mintCodeGraphRunCredential's VALIDATOR accepts; a response missing credential must fail the mint loudly, and a test asserts that arm rather than only the happy one.
  • The tarball intercept returns a 302 whose Location is what reaches MOTIR_INDEX_TARBALL_URL in the container spec, and NO response body is ever read — tests/helpers/indexFleet.ts's byte trap is the precedent and the same property must hold here.
  • Both variables are set on the WORKER process as well as the app webServer, and a test or a comment names why (the MOTIR-3498 shape).
  • E2E_TEST_CODE_GRAPH is absent by default, so no existing spec's server behaviour changes; tests/e2e/jobs-flow.spec.ts and tests/e2e/migrate-index-fleet.spec.ts still pass untouched.
  • The mock is refused outside the harness, exactly as its siblings are — a production process must not be able to install it.

Context refs

  • lib/test-github-repos-mock.ts · lib/test-billing-mock.ts — the two mocks to mirror
  • instrumentation.ts — the flag registration table
  • playwright.config.ts — the app webServer env block
  • tests/e2e/_helpers/job-worker-process.ts — the third process, and why it needs the same variables
  • tests/helpers/indexFleet.tsstubIndexFleet, the in-process equivalent this ports across the process boundary
  • lib/ai/motirAiClient.tsmintCodeGraphRunCredential and its response validator
  • lib/services/codeGraphIndexDispatchService.tsbootIndexContainer, the two calls
  • MOTIR-3498 — the webServer-only-variable defect this must not repeat