検索/ミューテーションテストのツールを、今のコードとテストを見て直す

ミューテーションテストのツールを、今のコードとテストを見て直す

ミューテーションテストのツールのコードを直す時に使う。先に今のコードとテストを確かめ、処理の順番や遅延処理を崩さず、壊れた変異を作らないミューテーターと、変異を見逃さないテストを書き、安全なレビュー・CI・リリースの手順で進める。

このAIルールを使うには
## 変更の前に、今のコードとテストと設計の記録を確かめる 現在のソースツリー、テスト、スキーマ、アーキテクチャ上の決定、コントリビューター向け文書を仕様として扱います。リポジトリの根拠と食い違う場合は、記憶、古い前提、一般的なフレームワークのパターンを信用しません。 機能追加や修正ごとに、次を行います。 1. 関連する名前、失敗、無効な入力、境界条件も含め、最寄りの単体・統合・E2E・アーキテクチャ・自己ミューテーションの各テストを検索します。 2. 担当する実装を読み、順序、既定値、失敗時の意味、性能上の制約を理解します。 3. コードだけでは意図が明確でない場合は、参照されている設計記録と上流の議論を読みます。 4. 実装前に対象を絞った失敗するケースを作成または移植し、その根拠へ関連付けます。 5. 自明でない仕組み、バージョンの境界、意図的な非互換性の理由を記録します。 プロジェクトの用語集を一貫して使います。mutationは別プロセスで適用される場合もある直列化可能な変更記述として扱い、mutatorは独立した適用可否チェックと変換の組み合わせとして扱います。 変更前に実行パイプラインを把握します。設定、ソースとカバレッジの収集、ASTの拡張、mutation生成、mutant実行、任意の追加分析、レポートは、順序を持つ責任境界です。変更によって文書化済みのパス、関門、規約が事実でなくなる場合は、同じ変更内でその文書も更新します。 ## 公開API・型・名前の付け方の決まりを守る - 本番コードをテストやベンチマークに依存させません。配布するテスト補助機能は明示的な本番側テスト名前空間に置き、依存方向を機械的に強制します。 - 公開APIを許可リストとして扱います。それ以外はすべて内部用とし、拡張点を文書化し、許可リストの変更は互換性に関わる事象として分類します。 - ハードな`final`は、決してモック化しないクラスだけに使います。1つのクラスをモック化するためだけにインターフェースを追加せず、モック対象のサービスは技術的に置き換えられる状態を保ちます。 - 関数と定数を一貫してimportします。`false`を返し得る呼び出しでは失敗時の意味を明示し、失敗を例外とみなす場合は安全なラッパーを、false値が必要な場合は明確に命名したunsafe aliasを使います。 - 型付き定数、明示的なoverrideマーカー、不変な値オブジェクトを優先します。自身のプロパティへメモ化するクラスをreadonlyにしません。 - 実行時assertionは、静的解析で証明できない不変条件だけに使います。assertionメッセージには、失敗が示す不具合を明記します。 - 名前で契約を表します。`find*`はnull許容値を返し、`get*`は値を保証し、名前付きコンストラクターはドメイン上の意味を表し、例外名は一般的な`Exception`で終わらせずドメイン上の失敗を表します。 - `Memoized*`はインスタンスごとに1回だけ行う、外部から見えない再利用に限定します。呼び出し側からkey、無効化、TTL、共有backendを観測できる場合だけ`Cached*`を使います。 - 純粋なstaticクラスや定数専用クラスはインスタンス化不能にします。依存性注入は、状態またはIOを持つ協調要素に限定します。 - 上流からコピーしたコードを識別可能にし、帰属表示を保ち、意図的なローカル差分をすべて文書化します。 ## 遅延処理を保ち、サブプロセスのコマンドを引数の配列で渡す - 高コストで再利用する値は、メモ化されたdeferred抽象で表します。生のgeneratorは1回しか走査できず、巻き戻し、件数取得、buffer化をしてはいけません。 - 厳密な1対1 mappingと、1対多のflat mappingを区別します。似たメソッド名によってstreamの要素数の違いを隠しません。 - 計測済みのhot pathで参照渡しのmutationを認めるのは、所有権とin-placeの効果が文書化されている場合だけです。 - 時刻やディスクのprimitiveを直接呼ばず、clockとfilesystemの抽象を注入します。テスト側で時刻とfilesystemの結果を制御できるようにします。 - サブプロセスのコマンドは、最初から最後まで文字列引数の配列で表します。配列の1要素を1つの引数として扱い、shell commandを事前に結合したり空白で分割されると仮定したりしません。 - 型付きgetterとlazy factoryを持つ明示的なservice registryを1つだけ維持します。利用側に型なしservice lookupを使わせず、到達不能な推測上のserviceを登録しません。 - 共有プロセスの状態を書き換える代わりに、containerをcloneして設定を再bindします。wiring closureが複雑になった場合は型付きbuilderを抽出します。 - 各fieldの意味に従って設定をmergeします。null許容のoverrideにはnull-coalescingを使い、どちらの入力元からもONを強制できるflagには論理ORを使います。これらを統一すると動作が気付かれないまま変わります。 - 相対的な出力先pathは設定ファイルの場所を基準に解決し、明示的に許可されたstream pathを維持し、利用者が意図した変更だけを処理するようversion controlのbaseを算出します。 ## ASTの処理の順番を守り、書き換える前のASTを変えない - visitorの登録順をアルゴリズムの一部として扱います。利用側より先に名前と親を解決し、テストを付与する前に除外を適用し、再利用するvisitorの状態をファイルごとにresetします。 - 型付きnode attribute accessorを通じて情報をやり取りします。除外ではnodeを対象外としてmarkするだけで、拡張処理中にnodeを取り除いたり置き換えたりしません。 - 高コストのカバレッジ参照は、対象nodeだけへ遅延付与します。カバレッジファイルの検出と、ファイルごとのテスト位置解析の間にある2段階の遅延性を保ちます。 - XML queryは、namespaceを認識する安全なwrapperを通します。順序を持つformat fallbackと、そのバージョン上の根拠を保ち、整理作業でまだ対応中の分岐を取り除きません。 - process runnerはstreamingを保ちます。利用者のテストが参照するworker識別子は、正確な1始まりのAPI値です。追加分析はscheduler固有の分岐ではなく、process containerのchainが担います。 - schedule計算では単位の意味を守ります。workのenqueueに要した時間はdurationであり、item countではありません。worker slotを再利用する前に、完了済みcontainerへの参照を解放します。 - バージョン固有のtest runner設定変更は、分離して組み合わせ可能にします。正本となる新しいcoverage設定、利用者のinclude filter、glob対応path、従来のdocument shapeを保ちます。 - mutation対象は字句位置とnode classで一致させます。mutation前に元のASTをcloneし、formatを保った出力のために元の字句列を維持し、元のtreeをbyte単位で同一に保ちます。 - mutation hash、field order、必須の位置attributeを外部向けの再実行契約として扱います。nodeを再構築するときはすべてのchildとattributeをコピーします。有効な置換はscalar、1つのnode、またはnode listのいずれでも構いません。 ## 壊れたコードや動きの変わらない変異を作らないミューテーターを作る mutatorの変更を完了するには、実装、すべての中央registryとprofile map、設定schema、正本となるtestを更新し、動作が利用者向けなら利用者向け文書も更新します。 - mutatorの公開面を最小限に保ちます。definition、安定したname、適用可否、変換だけを基本とし、mutatorが設定可能な場合に限り設定accessを加えます。 - definitionではmutation、category、diff例、利用者がmutantを検出する方法を説明します。 - 適用可否チェックでは、言語識別子をその言語本来の大小文字規則で比較し、入れ子のcontextが重要ならancestorをたどります。 - 構文error、必ずfatalになるerror、同値mutation、無限loop、別のmutatorがすでに担当するmutationを決して生成しません。 - 重複または同値になる可能性がある場合は理由を示し、名前付きの非mutation caseを追加します。 - 置換AST nodeを構築するときは、すべてのchildとmetadataをコピーし、attributeが気付かれないまま失われないようにします。 - 本番のtraversal harnessを通じてmutatorを実行します。適用可否やscopeの動作を迂回する使い捨てのtraverserを作りません。 - 期待値がない場合は明示的な「mutationしない」assertionとして扱い、data set名を仕様として読める形にします。 - mutation量が変わる場合は、mutatorごとの生成数、kill数、escape数、timeout数を報告します。 ## 毎回同じ結果になり、ミュータントを見逃さないテストを書く - 具体的なsource classごとに正本となるtestを用意し、各testに明示的なcoverage targetを指定します。 - IOを行うtestをintegration test、IOを行わないtestを非integration testとして分類します。この境界を両方向で強制します。 - testを決定論的に保ちます。生成した一時pathを使い、出力順に依存するfixtureではconcurrencyを固定し、environment variableをbackupし、存在しないpathをhard-codeしません。 - 説明的なcase名を持つstatic data providerを使います。引数の多いtest signatureにはnamed argumentとdefaultを優先しますが、異なるsubsetを使うtest間で1つのproviderを共有しません。 - 深いmock graphを、小さな不変builderまたはfakeへ置き換えます。緩いassertionをすり抜けてmutationがescapeしないよう、mock expectationで正確な引数を検証します。 - mutationを観測しやすいように本番コードを構成します。テスト不能なmagic literalより、early return、分離したboolean outcome、名前付き定数を優先します。 - 不足した依存を隠すconstructorのdefault引数を避けます。data providerのdefaultは、各caseが変化させる点だけを示せる場合に認めます。 - プロジェクトのvisitor harness、command harness、その他の本番動作を反映するfixtureを使います。対象を絞ったunit testを作るべき場面で、使い捨てのprobe scriptを書きません。 - escapeしたmutantは、test不足またはコード形状の問題を示します。本当にkill不能なcaseや性能だけに関わるcaseは、不自然なtestを書く代わりに議論します。 - 反復中は対象を絞ったtestを実行します。引き渡し前には、リポジトリのformatter、architectureとstatic analysisのreview、関連するintegration group、正本となるfull suiteを実行します。 ## バージョンごとの設定と代わりの処理を、コードの整理の時も残す - バージョンの境界値とfallback ladderを、それが守る動作とともに文書化します。最新の形だけに慣れているという理由でcompatibility matrixを単純化しません。 - 順序を持つ設定用mini-languageでは、keyを順番に処理し、後のentryを優先します。false sentinelは、入れ子のprofileを含め、そのkeyが登録した内容をすべて解除しなければなりません。 - 設定を組み合わせるときはmergeの方向と明示的な重複排除を保ちます。command lineでの選択はfile側の選択を置き換えられますが、global ignore ruleは維持します。 - 対応するmutator全体の正本を1つのregistryに集約し、そこから自動coverage checkを導出します。 - test名filterがregular expressionの上限に近づいたら、正確なfilterから安全なsupersetへ段階的に緩め、最後はfilterを省いてfull suiteを実行します。何も実行しない状態へ緩めてはいけません。 - 空の選択が有効な場合、runnerの「testが実行されなかった」という結果をpassとする対のruleも保ちます。 - debuggerを外すrestartの後は、現在load済みのextensionから能力を推測しません。restart handlerにskipしたversionを問い合わせ、必要なchildだけでcoverageを有効にします。 - memory limitの調整はstatic analysisの後に、一時的なchild設定だけへ適用し、明示的なguardを備えたbest-effort処理にします。 - process負荷の高い最適化は、採用前にbenchmarkします。runtimeまたはtoolchainのversionが変わったら、古いperformance workaroundを再検証します。 ## ロックファイルを守り、CIが通ったプルリクエストだけを取り込む 無人install中に固定済みlockfileを決して迂回しません。依存解決にlockfileの変更が必要なら停止し、明示的な承認を得ます。正確なversion、integrity hash、license compatibilityはsupply chainの境界です。 保護されたdefault branchへ直接pushしたり、branch protectionを迂回したり、管理者操作による強制mergeを行ったりしません。次の順序を使います。 1. 対象を絞ったfeature branchを作成します。 2. 最小限の一貫した変更を行い、対象を絞ったcheckを実行します。 3. リポジトリ指定のmessage conventionでcommitします。 4. branchをpushしてpull requestを開きます。 5. 必須のformat、type、unit、integration、E2Eの各checkがすべて完了するまで待ちます。 6. 失敗を迂回せず修正します。 7. 承認済みの方法でmergeし、branchを削除します。 refactoringとbehavior changeを混ぜません。修正したbugにはregression testが必要で、compatibility breakには明示的なrelease noteとmigration guideが必要です。利用者向けのbehaviorには、同期した利用者向け文書が必要です。 release automationにconventional commitからmetadataを導出させます。文書化されたoverrideは意図的な再分類にだけ使い、生成済みrelease artifactを手作業で書き換えません。 信頼できないcontributionのCIは、シークレットなし・読み取り専用の許可で実行します。シークレットを扱うpreviewでは、承認済みmaintainerがidentity gate付きcommandから明示的に起動しない限り、forkをskipします。 デプロイするexampleはすべてbuild matrixへ登録し、想定route、status、contentを検証するsmoke testを追加します。必須checkとデプロイ後の根拠がpassして初めて、releaseまたはdeploymentが完了します。