L2 v0.3.0 source

flaky-memory

Hangi testin hangi kod üzerinde başarısız olduğunu hatırlayan bir Claude Code Mod'u. Son 7 günde aynı kod üzerinde hem geçmiş hem de kalmış bir test başarısız olduğunda mod o Bash sonucuna bir not ekler, böylece model flaky bir test için kod değiştirmek yerine testi tekrar çalıştırır.

Ne yapar

Test çalıştıran bir Bash komutundan önce mod working tree'nin bir parmak izini alır: git rev-parse HEAD, git diff HEAD ve untracked dosyaların adları, 64 bit FNV-1a ile hash'lenir. Komuttan sonra çıktının geçti ya da kaldı diye adlandırdığı testleri okur ve test başına bir koşuyu o parmak iziyle saklar.

Bir parmak izinde hem bir geçiş hem de bir kalma varsa o test flaky'dir. Kod değişikliğinden sonraki bir kalma flaky değildir, çünkü parmak izi farklıdır.

Model, başarısız bir koşunun Bash sonucundan sonra şu notu okur:

flaky-memory: go:TestFlip failed 2 of 3 runs in the last 7 days and both passed and failed on the same code once. It may be flaky rather than broken by this change: run it again before you change code for it.

Aynı anda sizin için bir satır yazılır, böylece modele ne söylendiğini görürsünüz. Bu satır talimat cümlesi olmadan yalnız bulguyu taşır:

flaky-memory: go:TestFlip failed 2 of 3 runs in the last 7 days and both passed and failed on the same code once

sidebar açıkken bu satır oraya gider, stream'in içinde kırmızı bir kayıt olarak; transcript temiz kalır. Pencere artık o testin tek bir tree üzerinde hem geçişini hem kalmasını tutmadığında kayıt düşer ve yeşil bir kayıt bunu söyler:

flaky-memory: no longer flaky
go:TestFlip is no longer flaky: nothing in the last 7 days has it passing and failing on the same code

/flaky-memory reset kaydı kapanış satırı olmadan kaldırır, çünkü onu siz istediniz. Sidebar kapalıyken ya da o mod kurulu değilken yukarıdaki transcript satırı yazılır.

Test komutları

Bir Bash komutu, şunlardan birini içeriyorsa test komutudur: go test, pytest, python -m pytest, jest, vitest, bun test, cargo test, cargo nextest, phpunit (vendor/bin/phpunit de), npm test, pnpm test, yarn test (run ile de), bun run test, deno test, rspec, make test, mvn test, gradle test (./gradlew test de), dotnet test. Diğer komutlara dokunulmaz ve onlar için hiçbir git komutu çalışmaz.

Çıktının göstermesi gerekenler

Runner Kaldı Geçti
go test --- FAIL: TestX --- PASS: TestX (-v ile)
pytest FAILED path::test, ERROR path::test path::test PASSED (-v), PASSED path::test (-rA)
jest, vitest, bun , ×, , (fail) satırları , , (pass) satırları
cargo test test x ... FAILED test x ... ok
PHPUnit 1) Class::method yok
deno test name ... FAILED name ... ok
dotnet test Failed Name [12 ms] Passed Name [1 ms]
rspec rspec path:line # name yeniden koşma listesi yok
Maven surefire name(Class) Time elapsed … <<< FAILURE! yok
Gradle Class > test FAILED yok

Hiçbir geçen testi adlandırmayan bir koşu, 0 ile çıktığında, aynı komutun son başarısız koşusunda kalan testler için yine de geçiş sayılır. Yani -v olmadan go test ./..., PHPUnit, rspec, Maven ve Gradle de çalışır: kalmaları okunur ve 0 ile çıkan sonraki koşuları o testleri geçmiş sayar.

Yalnız pencere içinde kalan testler saklanır. Binlerce geçen testi olan bir suite hiçbir şey saklamaz. Her test son 7 günün en fazla 50 koşusunu tutar.

Komut

/flaky-memory                   bu repository'nin flaky testleri, en çok kalan üstte
/flaky-memory reset             bu repository'nin koşularını unut
/flaky-memory reset <test id>   tek bir testin koşularını unut, örneğin go:TestFlip
/flaky-memory on | off          test koşularını kaydet ya da kaydetme (varsayılan on); off saklanan koşuları korur

Repository, git common directory'dir; yani bir repository'nin worktree'leri koşularını paylaşır.

Kurulum

claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install flaky-memory@kilimcininkoroglu-mods

Function hook'lar early access. Flag olmadan hiçbir şey yüklenmez:

CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude

Flag'i kalıcı yapmak için ~/.claude/settings.json dosyasına ekleyin:

{ "env": { "CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1" } }

Kurulumdan sonra

Claude Code'u yeniden başlatın. Mod'un key'e ve ayara ihtiyacı yoktur. Bir git repository'sindeki ilk test koşusundan itibaren kaydeder.

Nereye uzanır

Claude Code 2.1.278 üzerinde claude plugin validate ile doğrulandı:

❯ ./register.ts hooks: session.start, command.run{command=flaky}, tool.call{tool=Bash}
❯ ./register.ts calls: $.clock.now (via learn, runCommand), $.command.register, $.process.run (via git), $.session.cwd, $.sidebar.clear (via dropEntry), $.sidebar.set (via toPerson), $.store.delete (via forget), $.store.get (via isEnabled, loadHistory), $.store.set (via forget, learn, runCommand), $.ui.log

Reach L2, git çalıştırır.

1. Okur:     her Bash test komutunun çıktısını; git üzerinden working tree'yi
2. Çalıştırır: git rev-parse, git diff HEAD ve git ls-files --others, salt okuma, argv ile, her test komutundan önce
3. Gönderir: flaky bir testin başarısız koşusundan sonra modele bir not ve size bir satır; makineden hiçbir şey çıkmaz
4. Saklar:   repository başına, $.store içinde: kalan her testin son 7 gündeki koşularını (zaman, parmak izi, geçti mi) ve her komutun en son kalan testlerini
5. Düşman girdi: test çıktısı güvenilmez metindir; sabit satır kalıplarına karşı eşleştirilir ve bir test adı yalnız saklanır ve geri yazılır, hiç çalıştırılmaz

Sınırlar

  • git repository'si dışında hiçbir şey kaydedilmez.
  • 4 MiB'ı aşan bir diff parmak izi almaz ve o koşu kaydedilmez.
  • Parmak izine untracked dosyaların yalnız adları girer, içerikleri değil. Untracked bir dosyanın içindeki değişiklik parmak izini değiştirmez.
  • Tree dışındaki durum (bir veritabanı, bir cache, /tmp altındaki bir dosya) parmak izinde değildir. Ona bağlı bir test flaky görünebilir.
  • Kesilen bir koşu ve arka plana alınan bir koşu kaydedilmez, çünkü çıktıları eksiktir.
  • Runner id ön ekleri (go:, pytest:, js:, cargo:, phpunit:, deno:, dotnet:, rspec:, maven:, gradle:) iki runner'ın adlarını ayırır. go id'si paket adı taşımaz, yani aynı test adına sahip iki paket tek id paylaşır.

Geliştirme

make install     # eslint, typescript-eslint, typescript
make lint        # complexity limiti 10, üstünde build'i düşürür
make typecheck   # /plugin-types ile üretilen .claude/types/ gerekir
make validate
make test        # claude plugin test

flaky-memory

A Claude Code Mod that remembers which tests failed on which code. When a test fails that has both passed and failed on the same code in the last 7 days, the mod adds a note to that Bash result, so the model runs the test again instead of changing code for a flaky test.

What it does

Before a Bash command that runs tests, the mod takes a fingerprint of the working tree: git rev-parse HEAD, git diff HEAD and the names of the untracked files, hashed with 64-bit FNV-1a. After the command, it reads the tests the output names as passed or failed and stores one run per test with that fingerprint.

A test is flaky when one fingerprint has both a pass and a failure of it. A failure after a code change is not flaky, because the fingerprint differs.

The model reads this note after the Bash result of a failed run:

flaky-memory: go:TestFlip failed 2 of 3 runs in the last 7 days and both passed and failed on the same code once. It may be flaky rather than broken by this change: run it again before you change code for it.

The same moment writes one line for you, so you see what the model was told. The line holds the finding alone, without the instruction:

flaky-memory: go:TestFlip failed 2 of 3 runs in the last 7 days and both passed and failed on the same code once

While the sidebar is open, that line goes there instead, as a red entry in its stream, and the transcript stays clean. When the window no longer holds a pass and a failure of that test on one tree, the entry goes down and a green one says so:

flaky-memory: no longer flaky
go:TestFlip is no longer flaky: nothing in the last 7 days has it passing and failing on the same code

/flaky-memory reset takes the entry down without a closing line, because you asked for it. With the sidebar closed, or without that mod installed, the transcript line is written as above.

Test commands

A Bash command is a test command when it contains one of: go test, pytest, python -m pytest, jest, vitest, bun test, cargo test, cargo nextest, phpunit (also vendor/bin/phpunit), npm test, pnpm test, yarn test (also with run), bun run test, deno test, rspec, make test, mvn test, gradle test (also ./gradlew test), dotnet test. Other commands pass through untouched, and no git command runs for them.

What the output must show

Runner Failed Passed
go test --- FAIL: TestX --- PASS: TestX (with -v)
pytest FAILED path::test, ERROR path::test path::test PASSED (-v), PASSED path::test (-rA)
jest, vitest, bun , ×, , (fail) lines , , (pass) lines
cargo test test x ... FAILED test x ... ok
PHPUnit 1) Class::method none
deno test name ... FAILED name ... ok
dotnet test Failed Name [12 ms] Passed Name [1 ms]
rspec the rspec path:line # name rerun list none
Maven surefire name(Class) Time elapsed … <<< FAILURE! none
Gradle Class > test FAILED none

A run that names no passing test still counts as a pass for the tests the same command failed at its last failing run, when it exits 0. So go test ./... without -v, PHPUnit, rspec, Maven and Gradle work too: their failures are read, and their next run that exits 0 counts those tests as passed.

Only tests that failed in the window are stored. A suite of thousands of passing tests stores nothing. Each test keeps at most 50 runs of the last 7 days.

Command

/flaky-memory                   the flaky tests of this repository, the most failing first
/flaky-memory reset             forget the runs of this repository
/flaky-memory reset <test id>   forget the runs of one test, for example go:TestFlip
/flaky-memory on | off          record test runs or not (on by default); off keeps the stored runs

The repository is the git common directory, so the worktrees of one repository share their runs.

Install

claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install flaky-memory@kilimcininkoroglu-mods

Function hooks are early access. Nothing loads without the flag:

CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude

To keep the flag on, add this to ~/.claude/settings.json:

{ "env": { "CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1" } }

After installing

Restart Claude Code. The mod needs no key and no setting. It records from the first test run in a git repository.

What it can reach

Validated with claude plugin validate on Claude Code 2.1.278:

❯ ./register.ts hooks: session.start, command.run{command=flaky}, tool.call{tool=Bash}
❯ ./register.ts calls: $.clock.now (via learn, runCommand), $.command.register, $.process.run (via git), $.session.cwd, $.sidebar.clear (via dropEntry), $.sidebar.set (via toPerson), $.store.delete (via forget), $.store.get (via isEnabled, loadHistory), $.store.set (via forget, learn, runCommand), $.ui.log

Reach L2, runs git.

1. Reads:    the output of each Bash test command; the working tree through git
2. Runs:     git rev-parse, git diff HEAD and git ls-files --others, read-only, by argv, before each test command
3. Sends:    a note to the model after a failed run of a flaky test, and one line to you; nothing leaves the machine
4. Persists: per repository, in $.store: each failed test's runs of the last 7 days (time, fingerprint, passed) and the tests each command failed last
5. Hostile input: test output is untrusted text; it is matched against fixed line patterns, and a test name is only stored and echoed back, never run

Limits

  • Outside a git repository nothing is recorded.
  • A diff over 4 MiB gets no fingerprint, and that run is not recorded.
  • Only the names of untracked files enter the fingerprint, not their content. A change inside an untracked file does not change the fingerprint.
  • State outside the tree (a database, a cache, a file under /tmp) is not in the fingerprint. A test that depends on it can show as flaky.
  • An interrupted run and a run sent to the background are not recorded, because their output is partial.
  • The runner id prefixes (go:, pytest:, js:, cargo:, phpunit:, deno:, dotnet:, rspec:, maven:, gradle:) keep the names of two runners apart. The go id has no package name, so two packages with the same test name share one id.

Development

make install     # eslint, typescript-eslint, typescript
make lint        # complexity limit 10, fails the build above it
make typecheck   # needs .claude/types/ from /plugin-types
make validate
make test        # claude plugin test