async function startTimespanGather(page, options = {}) {
  const {flags = {}, config} = options;
  log.setLevel(flags.logLevel || 'error');

  const {resolvedConfig} = await initializeConfig('timespan', config, flags);
  const driver = new Driver(page);
  await driver.connect();

  /** @type {Map<string, LH.ArbitraryEqualityMap>} */
  const computedCache = new Map();
  const artifactDefinitions = resolvedConfig.artifacts || [];
  const baseArtifacts = await getBaseArtifacts(resolvedConfig, driver, {gatherMode: 'timespan'});
  const artifactState = getEmptyArtifactState();
  /** @type {Omit<import('./runner-helpers.js').CollectPhaseArtifactOptions, 'phase'>} */
  const phaseOptions = {
    driver,
    page,
    artifactDefinitions,
    artifactState,
    baseArtifacts,
    computedCache,
    gatherMode: 'timespan',
    settings: resolvedConfig.settings,
  };

  await prepareTargetForTimespanMode(driver, resolvedConfig.settings);

  let pageNavigationDetected = false;
  function onFrameNavigated() {
    pageNavigationDetected = true;
  }

  driver.defaultSession.on('Page.frameNavigated', onFrameNavigated);

  const disableAsyncStacks = await enableAsyncStacks(driver.defaultSession);

  await collectPhaseArtifacts({phase: 'startInstrumentation', ...phaseOptions});
  await collectPhaseArtifacts({phase: 'startSensitiveInstrumentation', ...phaseOptions});

  return {
    async endTimespanGather() {
      const finalDisplayedUrl = await driver.url();

      const runnerOptions = {resolvedConfig, computedCache};
      const gatherFn = async () => {
        baseArtifacts.URL = {finalDisplayedUrl};

        await collectPhaseArtifacts({phase: 'stopSensitiveInstrumentation', ...phaseOptions});
        await collectPhaseArtifacts({phase: 'stopInstrumentation', ...phaseOptions});

        // bf-cache-failures can emit `Page.frameNavigated` at the end of the run.
        // This can cause us to issue protocol commands after the target closes.
        // We should disable our `Page.frameNavigated` handlers before that.
        await disableAsyncStacks();

        driver.defaultSession.off('Page.frameNavigated', onFrameNavigated);
        if (pageNavigationDetected) {
          baseArtifacts.LighthouseRunWarnings.push(str_(UIStrings.warningNavigationDetected));
        }

        await collectPhaseArtifacts({phase: 'getArtifact', ...phaseOptions});
        await driver.disconnect();

        const artifacts = await awaitArtifacts(artifactState);
        return finalizeArtifacts(baseArtifacts, artifacts);
      };

      const artifacts = await Runner.gather(gatherFn, runnerOptions);
      return {artifacts, runnerOptions};
    },
  };
}
@param {LH.Puppeteer.Page} page
@param {{config?: LH.Config, flags?: LH.Flags}} [options]
@return {Promise<{endTimespanGather(): Promise<LH.Gatherer.GatherResult>}>}