Draft: ci: weekly upstream divergence mining

What this adds

A weekly scheduled job that watches the two upstreams Artifact Registry reimplements and files issues for the fixes that apply to us.

AR reimplements behavior owned by the Container Registry (Go: OCI handlers, storage drivers, metadata DB) and the monolith's package registry (Ruby: Maven/npm semantics). Those upstreams keep fixing bugs their users hit in production, and nothing in our process notices — no MR fires, no pipeline runs against our copy, so we can silently re-ship a bug they already paid to find. The mapping is semantic (different files, names, architecture), so rules:changes cannot express it.

Each new upstream commit gets classified against our code:

Class Meaning Where it goes
shared our code has the same problem an issue in this project, with evidence
immune our design sidesteps it the run report
future we have not built that subsystem yet the run report
not-mapped outside the subsystem map the run report

A quiet week means no new divergence.

Scope

  • Periodic or manual, nothing else. The job runs on the weekly schedule (which sets DIVERGENCE_MINE=true), or on demand: Build → Pipelines → Run pipeline (or an API/glab trigger), then press play on divergence-mine. MR and branch pipelines never run it, so AR_DIVERGENCE_TOKEN cannot reach a fork pipeline.
  • Issues only. Findings become issues here, deduplicated on an upstream-sha:<sha> label, so re-runs never open a second issue for the same upstream commit — including one already closed. Low-confidence findings stay in the run report. Security-shaped findings route to the security mirror as confidential issues.
  • No code changes, no MRs. The two fix-proposing modes are disabled.
  • Defined locally. The job lives in this repository rather than being included from the tool's project, so the rules that decide when the token enters a pipeline are reviewable here. The tool enters as a pinned container image.
  • No new services or storage. State rides in the previous run's report.json artifact.
Path Purpose
.divergence/miner.yml Upstreams, lookback, mode, budgets
.divergence/map.yml Which upstream area maps to which of our packages
.divergence/ignore.yml Muted upstream commits
.gitlab/ci/divergence-miner.gitlab-ci.yml The job
docs/dev/divergence-mining.md How to operate and read it

Required before merge

  1. Create AR_DIVERGENCE_TOKEN — masked, and protected once the schedule targets a protected branch. Needs the api and ai_features scopes, and its owner needs a GitLab Duo seat: model calls route through Duo, so no vendor API key and no per-token bill.
  2. Decide the token identity. A gitlab-org/ops service account with a Duo seat is durable; a personal token ties the schedule to one person's credential and attributes every run to them. Requested in #393 — this MR can merge before that lands, but the weekly schedule should wait for it.
  3. Create the schedule after merge: cron 0 6 * * 1, target main, with the variable DIVERGENCE_MINE=true (the job's rules require it). Nothing runs until the schedule exists.
  4. Review .divergence/map.yml — it claims which upstream areas correspond to which of our packages, including the status: future entries that say "not built yet, do not look closer." This is the file that needs human judgment.
  5. Weigh the image source. The tool's image and project sit in a personal namespace today (divergence-miner); transfer to gitlab-org/ops plus a versioned catalog release are planned. AppSec flagged the original external include as the main risk here — vendoring the job locally addressed it, and reviewers should decide whether the pinned image is acceptable in the interim.
  6. Agree who triages. Issues are assigned to a random project maintainer by default; assignment_strategy also supports round-robin and none, and assignees takes an explicit list.

Evidence

A six-month run of this configuration (both upstreams, budgets raised to cover the window in one pass) processed 195 commits into 102 findings: 5 shared, 25 future, 56 immune, 15 not-mapped, nothing lost. The shared findings — filed to a scratch project during testing — included an S3 Stat() panic fix, a FIPS MD5-check fix from the monolith (a Ruby → Go finding), and a storage-driver conformance-test gap our suite is missing. Two look like real bugs in AR today. Findings the classifier could not fully verify were withheld rather than filed.

Cost: a handful of model calls per week (merges, dependency bumps, release and CI churn, and anything unmapped are dropped before any model call), one ~10–20 minute job, one upstream clone on disk at a time.

Related to #115 (closed)

Edited by Suleimi Ahmed

Merge request reports

Loading
Loading