Inspiration

AI coding agents are to code what factories were to physical goods. Since the first factories, machines have made products far faster than anyone could check each one by hand; coding agents did the same to software, writing merge requests far faster than anyone can review them.

Factories answered that bottleneck with sampling: inspect a sample instead of every unit, and when the sample shows a defect, fix the machine that made it, not only that unit. Code review has not made that shift. On my own projects, I still tried to read every line, and a review comment fixed only one merge request, so the next one came off the line with the same habits.

I built CodeTinder to bring both parts of that answer to merge requests. You judge a few one-change variants of one function, your swipes update a versioned principles doc in the repo that every agent reads, and an agent rewrites the rest of the MR to match. A sample never proves every line is fine, but each round adds evidence and builds confidence in what the agents produce.

What it does

On every GitLab merge request, CodeTinder runs one loop:

  1. Pick. An agent picks one changed function worth judging and writes 4 to 8 variants. Each variant is the whole function with one change, like a guard clause instead of a nested if. That edit isolates one principle: it applies a new rule the agent proposes, or follows (or deliberately breaks) one in .codetinder/principles.md. Every variant must pass the project's tests before it is shown; failures are dropped.
  2. Swipe. A comment on the MR links to the round on codetinder.dev, with a QR code for your phone. You see each variant as a diff and swipe like or dislike in a minute or two. Teammates can swipe the same round.
  3. Learn. Pressing Done replies on the MR, which starts the learn run. The run turns the swipes into changes to .codetinder/principles.md, a versioned Markdown doc in the repo that any agent can read: principles are added, strengthened, weakened, merged, narrowed with an exception, or dropped.
  4. Rewrite. An agent rewrites the MR's new code to follow the principles. The rewrite must also pass the tests before landing as a commit on the branch (or as a proposal MR) for you to review and merge.

A sweep brings the rest of the default branch in line: once enough code has landed, it opens an MR rewriting the functions that break the principles. Agents pick, generate, learn and rewrite; a person swipes and merges.

See it run

Here is one full loop from this repo on MR !152, a 1,800-line refactor judged by sampling one function:

  1. The round comment: the picked function and the link to its variants (sign in with GitLab to open them).
  2. Done's reply, which starts the learn run.
  3. The learn comment: two principles strengthened, two added, the rewrite, and the tests it passed.
  4. The rewrite commit and its green pipeline.

On the demo project todo-demo, a swiped round on !24 opened proposal MR !26, and a sweep (!34) brought four functions in line. A person merged both.

How we built it

Why it lives on GitLab Duo Agent Platform. Review happens on the merge request, so the loop lives there too:

  • One custom flow, released to the AI Catalog by CI. Its triggers are the only entry points: MR opened, MR marked ready, CodeTinder added as reviewer, a pipeline (for opt-in rounds on every push and for the sweep), and a mention. There is no webhook server.
  • The flow's Duo agent does the model work. It runs pick, learn, rewrite and sweep on GitLab's model, each with one correction turn if its answer fails a check. CodeTinder holds no model API key.
  • The CI runner is the sandbox that makes variants safe to show. It runs the project's own install and tests on every variant and every rewrite.
  • Only a person's comment starts a learn run. Done posts the reply as the developer, so every change to the principles doc traces back to someone's swipes, and every edit arrives as a commit or MR that a person merges.

Agents propose; code decides. Everything deterministic runs in flow-steps, a bundled TypeScript CLI the flow downloads from codetinder.dev. It reads the MR, checks the agent's pick, runs the tests, and applies learn's answer to the principles doc. Before testing a rewrite, flow-steps reverts any change to tests, CI, config or lockfiles, so the rewrite can't loosen the tests that judge it. Learn answers with at most 4 operations, and code refuses any that break a rule: narrowing a principle on fewer than 2 decisive swipes, or growing the doc past 20 principles.

The site. codetinder.dev is one Cloud Run service: an Astro landing page, a React swipe app and an API. It is written in TypeScript with Effect and stores every round and swipe in Firestore. Sign-in uses GitLab OAuth, and you can open a round only if GitLab lets you read that project's code. CI deploys the service with keyless workload identity federation.

Measuring the learner. learn-bench replays a project's judged rounds through learn, starting from an empty principles doc, and scores how well each version predicts the next round's swipes. A change to learn ships only if it scores no worse.

Challenges we ran into

  • The sandbox. The first six rounds on this repo failed with "Your install can't run in my sandbox". The runner image lacked libatomic, which the project's Node needs, and npm hid the failing script's output. Now every failure comment shows the command, its exit code and its output, and when a failure can be fixed in the agent config, CodeTinder opens a "Fix CodeTinder's sandbox" MR.
  • A successful request is not a successful run. On one MR, GitLab's workflow service returned 503, the learn session was dropped with no retry, and the page still said "Learned". Now the page shows "Learned" only when the run reports that it finished, and if a request goes unstarted, the page sends it once more. GitLab API calls taking 20+ seconds inside a 120-second step also needed retries sized to fit.
  • What a swipe means. The first 7 rounds showed rewrites of the whole function that mixed several changes; when I liked some and disliked others, a swipe couldn't say which. After a trial of 91 one-change variants, the next 51 rounds used that format (298 variants). Learn also stopped seeing what the generator meant each variant to test: it now reads the diff and the swipe, so it learns the preference the swipe actually showed.
  • A learner that only weakened. One contrary swipe used to weaken a rule everywhere. Now learn can narrow it with an exception instead, and boundary variants test exactly where that exception stops.
  • Tests are a gate, not a proof. A rewrite deleted a still-used type import and passed, because the tests ran without a typecheck; the MR's pipeline caught it. This repo's test command now includes the typecheck. A variant or rewrite is only as safe as the project's tests are thorough.

Accomplishments that we're proud of

  • CodeTinder reviewed its own build. Every MR in this repo got a round: 58 rounds on 34 MRs, with 21 swiped and learned (126 variants). 21 of 21 learn runs produced a rewrite: 20 commits on MR branches and one proposal MR.
  • A doc that changes its mind. 71 changes (25 added, 28 strengthened, 10 weakened, 3 reworded and 5 dropped) left 20 principles, every one learned from a swipe on this repo. This repo's coding agents are told to follow it.
  • Its own rounds shaped the product. Since CodeTinder started resting recently judged questions instead of repeating them, 88 of 120 variants (73%) have asked something new. The ledger in docs/dogfooding.md lists every finding and the change it led to.
  • No lock-in. The output is plain Markdown in your repo, reviewed like code and readable by any agent.

What we learned

  • Fix the machine, not the unit. A review comment fixes one MR; a principle the agent reads reaches the next one.
  • Models propose rules; code should hold the thresholds. A prompt can ask for enough evidence before changing a rule; only code can check that the evidence is there.
  • One question per variant. A judgement on one isolated change is quick, and it is honest.
  • Measure a learner by prediction. Scoring the principles doc against the next round's swipes turned changes to the learner into something I could compare, not argue about.
  • Say what the evidence covers. This is one developer's taste over 21 learned rounds on this repo and a demo project, not a study of review time saved.

What's next for CodeTinder: swipe for the love of code

  • Rounds that span several functions on a large MR.
  • Round history per project: every swipe and the principle changes it led to.
  • Swiping on the principle changes themselves, to refine them the same way.
  • Taste beyond functions: modules and folder structure.

Built With

  • astro
  • base-ui
  • biome
  • effect
  • firestore
  • gitlab
  • gitlab-ai-catalog
  • gitlab-ci
  • gitlab-duo-agent-platform
  • google-artifact-registry
  • google-cloud
  • google-cloud-run
  • node.js
  • oauth
  • pnpm
  • react
  • tailwindcss
  • tanstack-router
  • typescript
  • vitest
Share this project:

Updates

Submission history