check-pr
無料Check an externally created PR for coding rule violations against memory/INDEX.md atoms and docs/architecture/code-review-rules.md.
日本語の概要は準備中です。原文の説明を表示しています。
Stryker-driven test cull: delete slop, consolidate redundants, fill mutation-coverage gaps. Score up, test count and lines down. Use when the user says 'trim tests', '/trim-tests', or wants to clean up LLM-generated test bloat. No args picks the worst-scored file; argument scopes to a section, service, or file.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
LLM-generated test corpora accumulate slop — tests that touch lines without constraining behavior. Stryker is the arbiter for whether a deletion is safe. scripts/analyze-test-utility.ps1 is the arbiter for which tests are slop candidates.
Goal per run: mutation score same-or-higher, test count and line count lower.
The skill is iterative. Each phase has a verify-and-gate step; if the gate fails, the skill bisects, restores, or skips rather than committing a regression. The outer loop retries the whole flow up to 3 times with adjusted parameters before giving up on a file.
16, coverage-analysis offRun every Stryker config at concurrency: 16 and coverage-analysis: off. There are TWO independent failure modes here; both were demonstrated empirically on 2026-05-23 (see docs/testing/mutation-testing.md):
off, never perTest/all. perTest is NONDETERMINISTIC in this xUnit-v3/MTP environment: two runs over identical code gave 105 vs 356 killed (a 250-mutant swing). off over the same input gave 358 vs 358 (deterministic to ±1 timeout). A before/after gate can't use a tool that swings 250 mutants between identical runs — a bad run fakes a massive regression. off runs every test against every mutant (no coverage map to corrupt), so it's slower but reliable. We therefore have NO per-test attribution — the gate is "re-run and compare score + per-mutant kill diff," not a per-test kill table. (Two perTest runs once happened to agree with off, which briefly looked fine — it isn't; sample it enough and it breaks.)(no args) — daily mode. Run analyze-test-utility.ps1 to get the latest debt queue, pick the top "High-Confidence Test-Debt Candidate" not in the trimmed-file log.<section> — shifts, camps, teams, etc. Filter the debt queue to files matching that section's namespace.<service-name> — single service like CampService. Scope to its production .cs + matching *Tests.cs.--file <path> — explicit production file.scripts/analyze-test-utility.ps1 computes a DebtScore per test file based on signals that strongly correlate with slop: low assertion density, weak-assertion patterns (Should().NotBeNull(), Should().BeOfType(), Assert.True()), heavy mocking, reflection-shape checks, DI-shape checks, large size relative to assertions, missing production subject. It already classifies architecture ratchets and DI-cycle safety nets and excludes them from the high-confidence queue.
Don't reinvent these heuristics. Run the script, take its output, apply LLM judgment on individual tests inside the high-score files.
powershell -ExecutionPolicy Bypass -File scripts/analyze-test-utility.ps1 -Top 50 -StrykerReport local/stryker-runs/<latest>/reports/mutation-report.json
If no recent Stryker mutation report exists for the area, run one first (Phase 1 below) then re-run the utility with -StrykerReport. Otherwise the utility runs on file signals alone — fine but weaker.
Output lands in local/test-utility/test-utility-<timestamp>.{json,csv,md}. Read the JSON. From "High-Confidence Test-Debt Candidates," pick the target per the argument routing rules.
Find or generate a scoped Stryker config (existing ones live per section, e.g. tests/Humans.Users.Tests/stryker-profiles-config.json, tests/Humans.Shifts.Tests/stryker-shifts-config.json). If you must generate one, put it under local/stryker-trim/ (gitignored). Use coverage-analysis: off and concurrency: 16 (see the Hard constraint above). Example:
{
"stryker-config": {
"project": "Humans.<Section>.csproj",
"test-runner": "mtp",
"coverage-analysis": "off",
"mutation-level": "Standard",
"mutate": ["**/Services/<Area>/<File>.cs"],
"concurrency": 16,
"reporters": ["Json"],
"thresholds": {"high": 80, "low": 60, "break": 0}
}
}
Run:
Push-Location tests/Humans.<Section>.Tests
dotnet tool run dotnet-stryker --config-file <path>
Pop-Location
Parse StrykerOutput/<latest>/reports/mutation-report.json. Record:
mutationScore (percentage)The surviving-mutants list drives Phase 4. The score is the gate everywhere else. (With coverage-analysis: off there is no NoCoverage bucket — untested mutants are reported as Survived.)
From Phase 0's candidate file, identify the test methods inside that match slop patterns under LLM judgment (the file is already high-debt-score; we're picking which tests inside to drop). Patterns to flag:
.Received() / .DidNotReceive() assertions — verifies the mock was called, not the outcomeShould().NotBeNull(), Should().HaveCount(n), Should().BeOfType(), Should().Be(default) — shape, not behavior_DoesNotThrow where the production code can't throwTrap to avoid — same output, different code path. Two tests that produce the same observable output (same return value, same expected string) can still exercise different branches in the SUT (different if-arms, distinct early returns, distinct try/catch paths, distinct prefix-strip / fragment-handling branches). Before deleting a test as "cosmetic variant of another," briefly trace its inputs through the SUT. If it's the only test that enters a particular conditional branch, keep it — Stryker may not have a mutator covering that branch, and the score-delta gate will pass while the coverage gap quietly opens. Phrase the check explicitly to yourself: "is this test the only one whose inputs satisfy condition X in the SUT?" If yes, don't delete.
Form a deletion batch (start with everything flagged). Delete. Build. If build fails, the test was load-bearing in a way that wasn't visible — revert that specific test, try again.
Re-run Stryker. Compare both score AND per-mutant kill diff against baseline.
Bisection gate — two conditions, BOTH must hold:
If either gate fails, bisect:
After at most 3 bisection rounds, accept whatever deletion batch passes both gates. Commit:
test(<section>): drop N redundant tests in <ServiceName>Tests
That's the whole commit message. The numbers ARE the rationale.
Read remaining tests. Group by conceptual behavior. Candidates for xUnit [Theory]/[InlineData]:
_HappyPath_* variants that differ only in input_VariantA/B/C tests with identical assertion shapeThreshold: consolidate only when the group has 4+ members. Two cosmetic variants stay as two tests — the [Theory] ceremony isn't worth it below that.
Merge. Build. Re-run Stryker. If any previously-killed mutant is now surviving (compare mutant ID lists), restore the individual tests for that mutant. Re-run. Continue until score holds.
Commit:
test(<section>): consolidate N tests into theories in <ServiceName>Tests
From the Phase 1 surviving-mutants list, group by source location. For each cluster:
.Received() on a mock unless the mock call IS the contract (e.g., IEmailTransport.SendAsync).Skip mutants in: log messages, debug branches, ToString, generated code, anything in the project's Stryker exclusion list.
Cap: new tests at half the number deleted in Phase 2. If you can't lift the score that much within the cap, that's fine — the corpus was bloated for a reason.
Commit:
test(<section>): add N tests for surviving mutants in <ServiceName>Tests
Print to user:
<ServiceName>Tests
Mutation score: 67.3% → 81.2% (+13.9)
Tests: 103 → 62 (−41)
Lines: 1,247 → 718 (−529)
Mutants: 18 surviving → 6 surviving (−12)
Branch: trim-tests/<service>-<date>
Commits: 3
Then open the PR — don't ask first (always-open-a-pr). Title test(<section>): trim and consolidate <ServiceName>Tests. Body is the report block above. Nothing else.
After Phase 5, check the success criteria:
score_delta >= 0 (score didn't drop)test_count_delta < 0 (count went down)line_delta < 0 (lines went down)All three must hold. If they do, the run succeeded — commit, report, exit.
If one or more fail, this attempt didn't win. Don't commit the partial work. Decide the next move:
Max 3 outer iterations per file. If none succeeds, leave the work-in-progress branch but don't open a PR — report what was tried and stop.
[InlineData] consolidation only when the test count would be 4+ otherwise.If a change needs more than 2 lines of comment to explain, the change is too clever. Make the code more obvious instead.
origin/main in a worktree at .claude/worktrees/trim-tests-<area> locally, or in the repo root in a cloud run (memory/process/always-use-worktree.md)ServiceTestHarness, ServiceLocatorBuilder, the harness stub properties (AuditLog/Notifier/ShiftAuthInvalidator/AdminAuthorization). Don't add raw Substitute.For<> for those four interfaces.analyze-test-utility.ps1 -Top 50 (no Stryker report needed for the initial ranking)/trim-tests <name>.After a successful run, append the trimmed file's path + date + score delta to local/test-utility/trimmed-log.tsv (gitignored). On daily-mode runs, read this file to skip recently-trimmed targets.
dotnet stryker --version must work. If missing: dotnet tool restore (the repo has .config/dotnet-tools.json pinning Stryker 4.14.1).pwsh or PowerShell available for the utility script.coverage-analysis: off and concurrency: 16 in every Stryker config used. Verify before running.local/stryker-trim/Humans.Integration.Tests or Humans.Web.Tests — different shape, different skill if neededconcurrency above 16 to go faster — it starves the test host and manufactures false timeout-kills (see the Hard constraint).coverage-analysis: perTest/all to get richer data or faster runs — it's nondeterministic here and corrupts the gate (see the Hard constraint).まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Check an externally created PR for coding rule violations against memory/INDEX.md atoms and docs/architecture/code-review-rules.md.
日本語の概要は準備中です。原文の説明を表示しています。
Two-phase memory hygiene. Phase 1: scan external Claude Code memory (~/.claude/projects/<slug>/memory/) for durable rules to migrate into the repo and entries already duplicated by repo atoms. Phase 2: audit in-repo memory/, CLAUDE.md, docs/architecture/, and docs/sections/ for dead links, duplication, drift, and bloat.
日本語の概要は準備中です。原文の説明を表示しています。
Review, repair and steward the nightly Codex debt-sweep PR (branch codex/daily-debt/<date>). Judges every commit GOOD / REPAIR / REVERT, triages the bot review findings through /fix, lands one round-1 commit, files follow-up issues, then stewards the PR itself with a 3-round ceiling. Run by the 08:00 UTC cloud routine; also '/debt-review', '/debt-review 1789'.
日本語の概要は準備中です。原文の説明を表示しています。
Autonomous themed tech-debt cleanup. Reads docs/architecture/debt-ledger.yml, rotates to the least-recently-served debt theme, works it for a time budget (default 2h) or until drained, opens one PR, and asks judgment questions inline at the end. Use for daily debt burndown without Peter pointing at a target: grandfathered analyzer rules, architecture-test baselines, obsolete-field reads, cross-section stitching.
日本語の概要は準備中です。原文の説明を表示しています。
Scrap and regenerate the in-flight EF migrations on the current branch as one consolidated migration. Use when migrations have accumulated mid-development cruft (added/removed columns, stacked add-column-to-existing-table fixes, hand-edited SQL that needs to come out, schema changes that should have been one migration but ended up as five), or when the branch has merged main and its migrations are now stuck mid-chain so `dotnet ef migrations remove` is unsafe. Triggers on phrases like 'regen the migrations', 'redo these migrations', 'scrap and regen migrations', 'consolidate the in-flight migrations', 'redo the migration stack', or any time an agent hits the mid-chain situation in `memory/architecture/migration-regen-after-rebase.md`.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a sprint tracking issue (label `sprint`) on peterdrier/Humans needs working — routine-fired cloud runs, or any unattended session told to execute the sprint queue.
日本語の概要は準備中です。原文の説明を表示しています。