| 1 | Symbol-based detection only (ADR-001). Never decide semantics from a string name; ISymbol + SymbolEqualityComparer + MoqKnownSymbols always. A name check is allowed ONLY as a cheap pre-filter before an authoritative symbol check. Removing an existing string fallback requires proof the symbol path covers every case. | String matching breaks on aliases, renames, and user types named like Moq types (DoppelgangerTestHelper tests pin this); a wrong fallback silently changes rule behavior across all consumers. | Migration campaign #245→#1030 (final commit a974999). A fallback-removal attempt FAILED and was documented instead (5172cf3, #768); it succeeded only after full symbol coverage incl. IRaise`1 was added (35d363d, #770). src/BannedSymbols.txt bans Compilation.GetTypeByMetadataName (use KnownSymbols) and raw Diagnostic.Create (use DiagnosticExtensions.CreateDiagnostic). |
| 2 | AnalyzerReleases.Shipped.md is immutable outside release promotion. New rules and any ID/category/severity metadata change to an already-shipped rule go in Unshipped.md; a pure behavior/logic/FP fix changes none of the tracked fields, so it has NO AnalyzerReleases entry (see the change-class table above and moq-analyzers-rule-lifecycle Part 3). | Shipped is the historical record consumed by Roslyn's release-tracking analyzer; rewriting it lies about what past package versions contained. | .github/copilot-instructions.md:552: "Do not modify AnalyzerReleases.Shipped.md. This file is an immutable record of past releases." Prior mix-up: swapped file names, issue #983. |
| 3 | Dependency ceilings for shipped DLLs: System.Collections.Immutable ≤ 8.0.0, System.Reflection.Metadata ≤ 8.0.0, AnalyzerUtilities < 4.14.0, Roslyn pinned 4.8 (ADR-003/ADR-004). | Bundled assemblies must load inside a .NET 8 SDK / VS 2022 17.8 host. Exceeding host versions = CS8032, analyzers silently dead for every consumer on that SDK. | Incident #850: v0.4.0 transitively pushed SCI to 10.0.0.0 → CS8032 for all .NET 8 SDK users; fixed 38943ac (#888); release v0.4.1 existed for this. Now triple-enforced: ValidateAnalyzerHostCompatibility MSBuild target (build/targets/packaging/Packaging.targets), inline DLL-reference check in main.yml build job (~line 104), and the 9-way analyzer-load-test CI matrix (net8/9/10 CLI + net472/48/481 MSBuild). |
| 4 | Never raise S1135 (TODO tracking) above suggestion. TODO discipline is enforced by the todo-scanner instead: every TODO/FIXME/HACK/UNDONE must be issue-linked as TODO(#123). | Under PedanticMode all warnings are errors; making TODO a warning turns every tracked TODO into a CI build failure — the repo locked itself out of CI. | Commit 3d4f7ff (2026-03-06) raised it; reverted the next day by b1439ab; the build error was the codebase's own issue-linked TODO(#1012) comment (#1012 itself tracks an unrelated callback-validation enhancement, not this incident) — per moq-analyzers-failure-archaeology §4. Current setting: .editorconfig:420 dotnet_diagnostic.S1135.severity = suggestion. Scanner: build/scripts/todo-scanner/Scan-TodoComments.ps1, run in pre-push and tech-debt-tracker.yml. |
| 5 | PowerShell files are LF (*.ps1, *.psm1, *.psd1 have text eol=lf in .gitattributes), per ADR-010. Never override with CRLF. | CRLF makes #> block-comment terminators end in \r, which PowerShell cannot parse when hooks run from Git Bash / Unix shells — every push was blocked. | Issue #1081 (pre-push hook parse error in Scan-TodoComments.ps1); ADR-010 documents root cause and rejects the CRLF alternative explicitly. |
| 6 | Diagnostic spans are character-precise and pinned by tests. Test markup {|Moq1002:...|} asserts ID + exact span. A span test failure means your syntax-tree navigation is wrong — STOP after the first failure, re-derive the logic; escalate to a human after the second. Never "fix" the test to match your output. | The span is the user-visible squiggle; an off-by-one span on millions of consumer builds is a shipped bug. Historically, models that adjusted spans to make tests pass produced plausible-but-wrong analyzers. | .github/copilot-instructions.md:511-513: spans MUST be character-precise; a span test failure is a CRITICAL FAILURE; stop-and-escalate protocol verbatim there. |
| 7 | Every FP/FN fix ships with issue-linked regression tests in the same PR. | FP fixing historically converged only when each fix pinned its trigger; the Moq1203 saga took FIVE separate patches because each fix covered one syntactic wrapper (chaining 6ec810c #886 → parentheses c270302 #895 → sibling rules 894313b #907 → delegate overloads 0bef80b #919 → extension methods 5eec7e1 #1086). Syntactic wrappers (parens, extension methods, fluent chains) are a mandatory test axis. | The five commits above, plus Moq1302: fix 4b705e2 (#1017) was followed by a dedicated regression-suite commit 3399297 (#1020) referencing the originating report #1010. This is settled practice evidenced by history (not a literal CONTRIBUTING sentence — see UNVERIFIED note in Provenance). |
| 8 | AI-written code gets human-added adversarial cases before merge. An AI that writes both implementation and tests shares blind spots between them; a human must add boundary cases the tests did not think of: literals, captured locals, static/const members, method calls, external constants. | AI-authored Moq1302 (PR #511, Copilot-authored commit 458ca5d, 2025-06-25) shipped with comprehensive-looking tests, then produced live FPs on canonical LINQ-to-Mocks patterns (#1010). The human-authored follow-up suite 3399297 added exactly these categories: static lambda + external constant, captured local variable, != with external constant, chained properties. | Commits 458ca5d, 4b705e2, 3399297; the maintainer names "plausible-but-wrong AI code" as the costliest historical failure mode. |
| 9 | Span/allocation discipline on hot paths. Analyzers run per keystroke; no LINQ chains, ToArray(), or string formatting before the code knows a diagnostic will be reported. EnableConcurrentExecution + ConfigureGeneratedCodeAnalysis + IsMockReferenced() early exit in every Initialize; MoqKnownSymbols constructed once per CompilationStartAnalysisContext (ADR-006). | Per-operation allocation caused measurable IDE lag; the perf gate (perf job, ADR-008) is a required status check precisely to stop this class of regression. | Allocation-fix campaign: 9febdda (#1026, MoqKnownSymbols per compilation not per operation), 3b5ac71 (#1033, same hoist across 9 analyzers), 7595080 (#1050, array allocations in constructor matching). |
| 10 | No trial-and-error; STOP when unsure. If you cannot explain why your approach is correct, halt and ask — do not iterate until green. | Iterating-to-green produces code that passes existing tests while being semantically wrong (the exact failure mode behind rules 6–8). | .github/copilot-instructions.md "Escalation and Stop Conditions" (line ~72) and Quick Reference table: "Never guess or use trial-and-error; STOP if unsure." |