(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
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:
mintCodeGraphRunCredential->POST {MOTIR_AI_URL}/v1/code-graph/run-credential.MOTIR_AI_URL/MOTIR_AI_SERVICE_TOKENare set only inplaywright.acceptance.config.ts, and NO mock inlib/test-*mock*.tsintercepts that path.requireRepoTarballUrlResolver(getGitProvider('github'))-> an installation token minted from the App JWT, thenGET /repos/{owner}/{name}/tarball/{ref}read for its 302Location.GITHUB_APP_ID/GITHUB_APP_PRIVATE_KEYare set in NEITHER lane, andlib/test-github-repos-mock.tsintercepts 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:
lib/test-code-graph-mock.ts, behindE2E_TEST_CODE_GRAPH=1, intercepting theMOTIR_AI_URLorigin'sPOST /v1/code-graph/run-credentialwith a well-formedCodeGraphRunCredential(an opaque credential string and anexpiresAt), andapi.github.com'sGET /repos/{owner}/{name}/tarball/{ref}with a 302 carrying aLocation— the shaperesolveTarballUrlreads, not a body.- The lane's env:
MOTIR_AI_URL,MOTIR_AI_SERVICE_TOKEN,GITHUB_APP_IDand a generatedGITHUB_APP_PRIVATE_KEYon the app webServer, and — this is the half a reader will miss — on the WORKER process too.tests/e2e/_helpers/job-worker-process.tsspawns a THIRD process that inherits the RUNNER's env, notwebServer.env; MOTIR-3498 was filed becauseEMAIL_PROVIDERwas 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-refreshrun claimed by the lane's worker reachessettleIndexContainerand writes ONEsucceededjob_runper repo carrying oneoutput.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 missingcredentialmust fail the mint loudly, and a test asserts that arm rather than only the happy one. - The tarball intercept returns a 302 whose
Locationis what reachesMOTIR_INDEX_TARBALL_URLin 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_GRAPHis absent by default, so no existing spec's server behaviour changes;tests/e2e/jobs-flow.spec.tsandtests/e2e/migrate-index-fleet.spec.tsstill 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 mirrorinstrumentation.ts— the flag registration tableplaywright.config.ts— the app webServer env blocktests/e2e/_helpers/job-worker-process.ts— the third process, and why it needs the same variablestests/helpers/indexFleet.ts—stubIndexFleet, the in-process equivalent this ports across the process boundarylib/ai/motirAiClient.ts—mintCodeGraphRunCredentialand its response validatorlib/services/codeGraphIndexDispatchService.ts—bootIndexContainer, the two calls- MOTIR-3498 — the webServer-only-variable defect this must not repeat