[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-dotnet-grade-tests":3,"mdc-j57xrk-key":34,"related-org-dotnet-grade-tests":2884,"related-repo-dotnet-grade-tests":3046},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":22,"repoUrl":23,"updatedAt":24,"license":25,"forks":26,"topics":27,"repo":29,"sourceUrl":32,"mdContent":33},"grade-tests","grade test method quality","Grades a specified set of test methods individually and produces a concise table mapping each test (fully-qualified name) to a letter grade (A–F), a score band, and a one-line note — designed to be posted as a PR comment. Use when the caller wants per-test feedback on a curated list of methods (for example, the new or modified tests in a pull request), not a suite-wide audit. Polyglot: .NET, Python, TS\u002FJS, Java, Go, Ruby, Rust, Swift, Kotlin, PowerShell, C++. Input is a list of test methods (or method bodies \u002F file+line spans); output is a compact markdown table plus a short summary. DO NOT USE FOR: full suite audits (use test-quality-auditor agent or test-anti-patterns), writing new tests (use code-testing-generator agent or writing-mstest-tests), fixing failures, or measuring code coverage.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"dotnet",".NET (Microsoft)","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fdotnet.png",[12,16,19],{"name":13,"slug":14,"type":15},"QA","qa","tag",{"name":17,"slug":18,"type":15},"Engineering","engineering",{"name":20,"slug":21,"type":15},"Testing","testing",4576,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills","2026-07-25T05:31:29.31054","MIT",332,[28],"agent-skills",{"repoUrl":23,"stars":22,"forks":26,"topics":30,"description":31},[28],"Repository for skills to assist AI coding agents with .NET and C#","https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills\u002Ftree\u002FHEAD\u002Fplugins\u002Fdotnet-test\u002Fskills\u002Fgrade-tests","---\nname: grade-tests\ndescription: >\n  Grades a specified set of test methods individually and produces a concise\n  table mapping each test (fully-qualified name) to a letter grade (A–F), a\n  score band, and a one-line note — designed to be posted as a PR comment.\n  Use when the caller wants per-test feedback on a curated list of methods\n  (for example, the new or modified tests in a pull request), not a\n  suite-wide audit. Polyglot: .NET, Python, TS\u002FJS, Java, Go, Ruby, Rust,\n  Swift, Kotlin, PowerShell, C++. Input is a list of test methods (or method\n  bodies \u002F file+line spans); output is a compact markdown table plus a short\n  summary. DO NOT USE FOR:\n  full suite audits (use test-quality-auditor agent or test-anti-patterns),\n  writing new tests (use code-testing-generator agent or writing-mstest-tests),\n  fixing failures, or measuring code coverage.\nlicense: MIT\n---\n\n# Grade Tests\n\nGrade a curated list of test methods and produce a compact, PR-comment-friendly\nreport: one row per test method with a letter grade, a score band, and a\none-line note explaining the grade. The skill **does not discover tests on its\nown** — the caller (typically a PR automation workflow or a human reviewer\nholding a specific list) provides the test methods to grade.\n\n> **Language-specific guidance**: Call the `test-analysis-extensions` skill\n> to discover available extension files, then read the file matching the\n> target codebase's language and framework (e.g., `extensions\u002Fdotnet.md`,\n> `extensions\u002Fpython.md`, `extensions\u002Ftypescript.md`, `extensions\u002Fgo.md`).\n> You MUST read the relevant extension file before scoring assertions or\n> anti-patterns, because assertion APIs and idiomatic patterns differ\n> significantly across frameworks.\n\n## Why a Per-Test Grade\n\nSuite-wide audits (`test-anti-patterns`, `assertion-quality`,\n`test-smell-detection`) produce excellent diagnostic reports, but they are\nhard to consume as a short PR comment. Reviewers of a PR mostly want to know:\n*for the tests this PR adds or changes, are they good?* This skill answers\nthat question with a one-row-per-test verdict that fits in a comment table.\n\n## When to Use\n\n- A PR automation workflow needs to post a comment grading the tests\n  introduced or modified in a pull request.\n- A reviewer has a specific list of tests (a file, a class, a method list,\n  or a diff hunk) and wants a per-test verdict rather than a suite report.\n- A maintainer wants to triage which of N tests in a contribution deserve\n  follow-up improvements.\n\n## When Not to Use\n\n- The caller wants a full suite audit or comparative metrics — use\n  `test-anti-patterns` (pragmatic) or `test-smell-detection` (formal) and\n  let the `test-quality-auditor` agent orchestrate.\n- The caller wants to *write* new tests — use `code-testing-generator`\n  (any language) or `writing-mstest-tests` (MSTest specifically).\n- The caller wants to measure code coverage or CRAP scores — use\n  `coverage-analysis` or `crap-score` (.NET only).\n- The caller wants to fix issues directly in test code — invoke the\n  appropriate editing skill.\n- No specific list of tests is provided. Do **not** try to grade every test\n  in the workspace; ask the caller for an explicit list or scope.\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Test methods | Yes | A scope to grade. Provide one of: (a) an explicit list of test method names (fully-qualified, e.g. `Namespace.ClassName.TestMethodName`); (b) one or more file paths plus an explicit instruction to grade every test declared in those files; or (c) a diff hunk \u002F PR identifier whose changed tests should be graded. File paths are recommended but optional when method names are unambiguous in the workspace. Ambiguous requests like *\"grade my tests\"* with no scope are rejected up-front (see Step 0); this skill is for curated input and does not auto-grade an entire workspace. |\n| Test bodies \u002F spans | Recommended | The exact source lines for each test method. If omitted, read them from the listed files. |\n| Production code | No | The code under test, for judging whether assertions cover the meaningful behaviors. When unavailable, mark relevant findings as \"Unverified\" rather than guessing. |\n| Diff context | No | When grading PR changes, the unified diff for each test method helps focus on what actually changed. |\n\n### Step 0: Validate the input\n\nBefore doing anything else, check that the caller provided one of:\n\n1. An explicit list of test method names, **or**\n2. One or more file paths plus an explicit instruction to grade every test\n   declared in those files (e.g., \"grade every test in `OrderTests.cs`\"), **or**\n3. A diff hunk or PR identifier whose changed tests should be graded.\n\nIf the request is ambiguous (e.g., *\"Grade my tests\"*, *\"Are these tests\nany good?\"* with no scope, *\"Review the test suite\"*), **do not load\nextensions, do not read files, and do not grade anything**. Reply with a\nshort message asking the caller to provide an explicit list \u002F file(s) \u002F\ndiff, and optionally point them at `test-quality-auditor` agent or\n`test-anti-patterns` skill for full-suite analysis. Stop there.\n\n## Workflow\n\n### Step 1: Detect language and load extension\n\nIdentify the target codebase's language and test framework from the file\nextensions and the test method markers in the provided list. Call the\n`test-analysis-extensions` skill and read the matching extension file (e.g.,\n`extensions\u002Fdotnet.md` for MSTest\u002FxUnit\u002FNUnit\u002FTUnit, `extensions\u002Fpython.md`\nfor pytest, `extensions\u002Ftypescript.md` for Jest\u002FVitest, `extensions\u002Fgo.md`\nfor the standard `testing` package). If the input contains tests from\nmultiple languages, load each relevant extension and grade each test using\nits language's conventions.\n\n### Step 2: Resolve the test bodies\n\nFor each entry in the input list:\n\n1. If the test body is provided inline, use it directly.\n2. Otherwise read the file at the given path and locate the method by its\n   fully-qualified name. Capture the full method body, including attributes\n   \u002F decorators \u002F fixtures and any helper code that the test calls.\n3. If a method cannot be found, record it as `N\u002FA — method not found` and\n   continue. Never invent a body to grade.\n\n### Step 3: Score each test\n\nStart every test at grade **A (score band 90–100)**, then apply deductions\nstrictly for **observable issues** in the captured body. Do **not** deduct\nfor hypothetical concerns (e.g., \"could have more negative assertions\")\nunless the production code clearly demands them and the production code is\navailable.\n\nWhen production code is unavailable, grade observable issues in the test body\nnormally, but do not infer missing behaviors or deduct for them. State\n`Production-dependent behavior coverage: Unverified` once in the summary so the\nreader can distinguish test-body findings from claims that require source code.\n\n#### Three sub-dimensions\n\nCompute three sub-grades (each A–F) that together drive the overall grade.\n\n##### A. Assertion strength\n\nRead the loaded language extension's assertion API list and classify every\nassertion in the test body. Score from highest to lowest:\n\n| Sub-grade | Pattern |\n|-----------|---------|\n| **A** | At least one meaningful value assertion (equality \u002F structural \u002F exception \u002F state) plus, where appropriate, additional checks (negative, type, collection contents). Mock-call verifications (`Verify`, `toHaveBeenCalledWith`, `Should -Invoke`) and bare assertion forms (pytest `assert`, Go `if got != want { t.Errorf(...) }`, Rust `assert!()`) count as real assertions. |\n| **B** | One clear meaningful assertion that verifies the behavior under test. |\n| **C** | Only trivial assertions (single `IsNotNull` \u002F `toBeDefined` \u002F `assert x is not None`), or assertions that check a single field while the operation produces a richer result. |\n| **D** | One self-referential \u002F tautological assertion (`Assert.AreEqual(x, x)`, `assert dto.name == dto.name`, round-trip identity without a non-trivial input), or broad exception assertions (`Assert.ThrowsException\u003CException>`). |\n| **F** | No assertions at all; **all** assertions are always-true literals (`Assert.IsTrue(true)`, `assert True`, `expect(true).toBe(true)`) — these verify nothing and are equivalent to having no assertions; or all assertions are silently un-awaited (e.g., `expect(promise).resolves.toBe(x)` without `await`\u002F`return`, async TUnit\u002FxUnit `Assert.ThrowsAsync` without `await`, pytest-asyncio with un-awaited coroutine). |\n\nException and error-path tests (`Assert.ThrowsException\u003CT>`, constrained\n`pytest.raises`, `expect(fn).toThrow`, `assertThrows`, `#[should_panic]`,\n`Should -Throw`, `EXPECT_THROW`, or Go code that verifies an expected non-nil\nerror) are complete on their own. Give Assertion strength **A** when the test\nchecks the exact promised error condition for its stated scope. Do not deduct\nfor having only that assertion, and do not require an error-message assertion\nunless the message is part of the documented contract. A Go happy-path test\nthat only checks `err == nil` while discarding a meaningful returned value is\nstill **C** because it does not verify the successful result.\n\n##### B. Structure & focus\n\n| Sub-grade | Pattern |\n|-----------|---------|\n| **A** | Clear Arrange-Act-Assert (or Given-When-Then) separation. Single behavior under test. Body under ~30 lines. Setup uses framework conventions. |\n| **B** | One mild structural issue (slightly long body, missing blank lines between phases) but intent is clear. |\n| **C** | Multiple behaviors mixed in one test, or AAA phases interleaved enough to slow comprehension. |\n| **D** | Conditional logic in the test (`if`\u002F`switch` driving assertions) — except for idiomatic Go\u002FRust table-driven sub-test loops; or test relies on previous test state (ordering dependency). |\n| **F** | Test exceeds ~60 lines and verifies multiple unrelated behaviors; or shares mutable state with other tests through statics\u002Fglobals without reset. |\n\n##### C. Anti-pattern hygiene\n\nScan against the catalog below. The Anti-pattern sub-grade is computed\nin two passes and combined deterministically:\n\n1. **Hard ceiling pass.** Every **Critical** or **High** finding sets a\n   maximum sub-grade (F, D, or C as labeled). Take the **worst** ceiling\n   across all matched Critical\u002FHigh findings — these do not accumulate\n   (a single F finding caps the sub-grade at F regardless of how many\n   other Critical\u002FHigh findings are present).\n2. **Medium-deduction pass.** Start from **A**, then for each **Medium**\n   finding deduct one sub-grade level (A→B, B→C, C→D, D→F). These do\n   accumulate across findings.\n\nThe final Anti-pattern sub-grade is the **worse** of the two passes\n(i.e., `min(hard_ceiling, A − medium_count)`). **Low** findings never\naffect the grade — mention them in the note only.\n\nExamples (Critical\u002FHigh and Medium counts → Anti-pattern sub-grade):\n\n- Zero Critical\u002FHigh, 1 Medium → **B** (A − 1)\n- Zero Critical\u002FHigh, 3 Medium → **D** (A − 3)\n- One C-ceiling (e.g., over-mocking), 0 Medium → **C**\n- One C-ceiling, 2 Medium → **D** (`min(C, A − 2 = C) = C`, but a third Medium would tip to **D**)\n- One F-finding (e.g., swallowed exception) plus any number of Medium → **F**\n\n**Critical (drop straight to F or D)**\n\n- No assertions at all → F (also drives Assertion sub-grade to F)\n- Swallowed exceptions: `try { … } catch { }` (.NET), bare `except: pass`\n  (Python), `try { … } catch (e) {}` (JS\u002FTS\u002FJava), `defer recover()`\n  without re-panic (Go), `rescue StandardError` with no assertion (Ruby),\n  empty `catch` (Kotlin\u002FSwift) → F\n- Assert-in-catch pattern (`Assert.Fail(ex.Message)` instead of\n  `Assert.ThrowsException`) → D\n- Always-true literal assertions (`Assert.IsTrue(true)`, `assert True`,\n  `expect(true).toBe(true)`) → **F** (verifies nothing; also drives\n  Assertion sub-grade to F)\n- Self-referential \u002F tautological assertions on bound values\n  (`Assert.AreEqual(x, x)`, `assert dto.name == dto.name`) → D\n- Commented-out assertions → D\n\n**High (drop one or two sub-grades)**\n\n- Wall-clock sleep used for synchronization: `Thread.Sleep`, `Task.Delay`,\n  `time.sleep`, `setTimeout`-based wait, `Thread.sleep`, `time.Sleep`,\n  `sleep`, `std::thread::sleep`, `Start-Sleep`,\n  `std::this_thread::sleep_for` (in a unit test) → D\n- Unseeded randomness, wall-clock reads without abstraction\n  (`DateTime.Now`, `datetime.now()`, `Date.now()`,\n  `System.currentTimeMillis()`, `time.Now()`, `Time.now`,\n  `Instant::now()`, `Get-Date`, `system_clock::now`) → D\n- Hard-coded environment-dependent paths (`C:\\…`, `\u002Ftmp\u002F…`, network hosts) → D\n- Ordering dependency on mutable static \u002F package globals → D\n- Broad exception assertion (`Assert.ThrowsException\u003CException>`,\n  `pytest.raises(Exception)`, `expect(fn).toThrow(Error)` without matcher,\n  `#[should_panic]` without `expected = \"…\"`, `Should -Throw` without\n  `-ExpectedMessage`, `EXPECT_ANY_THROW`) → C\n- Over-mocking: more mock setup lines than test logic, or verifying exact\n  call sequences instead of outcomes → C\n- Implementation coupling: reflection on private members, casting to\n  internal types to access state → C\n\n**Medium (drop one sub-grade)**\n\n- Poor name: `Test1`, `TestMethod`, `test`, single-word name that says\n  nothing about scenario or expected outcome (judge against the language\n  extension's convention) → drop one sub-grade\n- Magic values: unexplained `42`, `\"foo\"`, `0x1234` in arrange\u002Fassert\n  without naming or comment → drop one sub-grade\n- Giant test (>30 lines covering a single behavior) → drop one sub-grade\n- Assertion messages that just repeat the assertion text → drop one sub-grade\n- Missing AAA \u002F GWT separation when the test is non-trivial → drop one sub-grade\n\n**Low (note only, no deduction)**\n\n- Unused setup\u002Fteardown hooks; print debugging left in (`Console.WriteLine`,\n  `print`, `console.log`, `System.out.println`, `fmt.Println`, `puts`,\n  `dbg!`, `Write-Host`, `std::cout`); inconsistent naming versus siblings;\n  leftover TODO comments. Mention in the note column but do not deduct.\n\n#### Combining sub-grades\n\nConvert sub-grades to numeric points: A=4, B=3, C=2, D=1, F=0.\n- **Overall score band** = weighted average:\n  `0.45 × Assertion + 0.30 × Anti-pattern + 0.25 × Structure`\n- Map to letter:\n  - ≥ 3.5 → **A** (band 90–100)\n  - ≥ 2.8 → **B** (band 80–89)\n  - ≥ 2.0 → **C** (band 70–79)\n  - ≥ 1.2 → **D** (band 60–69)\n  - \u003C 1.2 → **F** (band 0–59)\n- The overall grade is **capped at the worst sub-grade** — if any sub-grade\n  is **F**, the overall grade is **F**; if the worst sub-grade is **D**,\n  the overall grade is at most **D**; and so on. A test that fails on any\n  one dimension cannot earn a higher overall grade than that dimension.\n\nReport the **letter grade** and the **score band** (not a single 0–100\nnumber). False precision invites bikeshedding; bands keep the conversation\nfocused on the rubric.\n\n### Step 4: Build the note\n\nThe note column is one short sentence (target ≤ 120 characters). State the\nsingle most important reason for the grade. Examples:\n\n- A (90–100): `Clear AAA structure; equality + exception assertions on the public contract.`\n- B (80–89): `Good assertion variety, mildly long body — consider splitting into per-condition tests.`\n- C (70–79): `Only checks IsNotNull on the result; no value verification.`\n- D (60–69): `Self-referential assertion: round-trip identity verifies plumbing, not transformation.`\n- F (0–59): `No assertions — test executes the method but never verifies anything.`\n\nIf a test gets A with no notable issues, the note may simply be\n`No issues found.` — do not invent weaknesses to justify the grade.\n\n### Step 5: Report\n\nProduce two sections.\n\n#### 1. Summary\n\nA short paragraph (2–4 sentences) covering: total tests graded, grade\ndistribution, most common issue, and the single most important\nrecommendation.\n\n#### 2. Per-test table\n\n```markdown\n| Test | Grade | Band | Notes |\n|------|-------|------|-------|\n| `Namespace.ClassName.Test_Method_Condition_Expected` | A | 90–100 | Clear AAA; equality + exception assertions. |\n| `Namespace.ClassName.Test_Other` | C | 70–79 | Only `IsNotNull` — no value verification. |\n| `Namespace.ClassName.Test_Old` | F | 0–59 | No assertions. |\n```\n\n**Caps and ordering**:\n- If the table would exceed **50 rows**, show all tests graded below **B**\n  first (worst to best), then a sample of the best tests, and wrap any\n  overflow in a collapsed `\u003Cdetails>` block.\n- Within the same grade, order by file path then by method name for\n  determinism.\n- If the diff context is provided, prefix each test name with a `(new)` or\n  `(modified)` marker.\n\nIf multiple languages are present, produce one table per language and\nprefix each section with the language name and framework.\n\n## Validation\n\n- [ ] Every test in the input list appears in the table (or is recorded as\n      `N\u002FA — method not found`).\n- [ ] Every grade is justified by at least one observable signal in the\n      captured body — no speculative deductions.\n- [ ] Trivial-assertion tests are flagged only when the **only** assertion\n      is trivial (a null check before a meaningful assertion is not trivial).\n- [ ] Exception-only tests are not penalized for low assertion count.\n- [ ] Mock-call verifications and bare assertion forms count as real\n      assertions of the appropriate category.\n- [ ] Boolean assertions on meaningful properties (`Assert.IsTrue(result.IsValid)`)\n      are not classified as always-true; only literal `true`\u002F`false` constants are.\n- [ ] Self-referential assertions are flagged separately from normal\n      equality assertions.\n- [ ] Idiomatic patterns are not flagged: Go\u002FRust table-driven sub-tests,\n      pytest bare `assert`, Go `if got != want { t.Errorf(...) }`,\n      JS\u002FTS `expect(mock).toHaveBeenCalledWith(...)`.\n- [ ] Async test pitfalls (un-awaited `resolves`\u002F`rejects`\u002F`ThrowsAsync`,\n      pytest-asyncio without `await`) drop the Assertion sub-grade to F.\n- [ ] The summary leads with the highest-leverage observation, not a recap\n      of the table.\n\n## Common Pitfalls\n\n| Pitfall | Solution |\n|---------|----------|\n| Grading every test in the workspace when no list is provided | Ask the caller for the explicit list; this skill is for curated input. |\n| Inflating deductions to justify the grade | Start at A; deduct only for observable issues. |\n| Penalizing exception tests for low assertion count | Exception assertions are complete on their own. |\n| Downgrading a focused Go error-path test because it checks only `err != nil` | Expected-error existence is the observable contract for that scope; keep it at A unless the production contract requires a specific error identity or message. |\n| Treating `IsNotNull` before a value assertion as trivial | Only flag when the null check is the **only** assertion. |\n| Treating any Boolean assertion as effectively assertion-free | Only always-true literals (`Assert.IsTrue(true)`, `assert True`) are; meaningful `Assert.IsTrue(result.IsValid)` is a real assertion. |\n| Flagging Go\u002FRust table-driven loops as conditional logic | They are idiomatic; do not deduct. |\n| Treating pytest bare `assert` or Go `if got != want { t.Error… }` as missing-framework | Both are canonical; count in the correct assertion category. |\n| Penalizing tests when production code is unavailable | Mark concerns about uncovered behaviors as `Unverified` and do not deduct. |\n| Using a fake-precise score (e.g., 87\u002F100) | Use the score band only — 90–100, 80–89, 70–79, 60–69, 0–59. |\n| Spilling a 500-row table into a PR comment | Apply the row cap from Step 5; collapse extras into `\u003Cdetails>`. |\n| Re-reporting an existing finding three times under different categories | Pick the most fitting category and report once. |\n| Inventing weaknesses for A-grade tests to make the note \"balanced\" | If a test is clean, the note may simply read `No issues found.` |\n",{"data":35,"body":36},{"name":4,"description":6,"license":25},{"type":37,"children":38},"root",[39,47,61,115,122,157,163,183,189,285,291,410,417,422,458,504,510,516,563,569,574,600,606,631,644,651,656,663,668,929,1005,1011,1121,1127,1132,1188,1215,1220,1288,1296,1430,1438,1682,1690,1762,1770,1842,1848,1853,1971,1990,1996,2001,2059,2072,2078,2083,2089,2094,2100,2347,2357,2412,2417,2423,2608,2614,2878],{"type":40,"tag":41,"props":42,"children":43},"element","h1",{"id":4},[44],{"type":45,"value":46},"text","Grade Tests",{"type":40,"tag":48,"props":49,"children":50},"p",{},[51,53,59],{"type":45,"value":52},"Grade a curated list of test methods and produce a compact, PR-comment-friendly\nreport: one row per test method with a letter grade, a score band, and a\none-line note explaining the grade. The skill ",{"type":40,"tag":54,"props":55,"children":56},"strong",{},[57],{"type":45,"value":58},"does not discover tests on its\nown",{"type":45,"value":60}," — the caller (typically a PR automation workflow or a human reviewer\nholding a specific list) provides the test methods to grade.",{"type":40,"tag":62,"props":63,"children":64},"blockquote",{},[65],{"type":40,"tag":48,"props":66,"children":67},{},[68,73,75,82,84,90,92,98,100,106,107,113],{"type":40,"tag":54,"props":69,"children":70},{},[71],{"type":45,"value":72},"Language-specific guidance",{"type":45,"value":74},": Call the ",{"type":40,"tag":76,"props":77,"children":79},"code",{"className":78},[],[80],{"type":45,"value":81},"test-analysis-extensions",{"type":45,"value":83}," skill\nto discover available extension files, then read the file matching the\ntarget codebase's language and framework (e.g., ",{"type":40,"tag":76,"props":85,"children":87},{"className":86},[],[88],{"type":45,"value":89},"extensions\u002Fdotnet.md",{"type":45,"value":91},",\n",{"type":40,"tag":76,"props":93,"children":95},{"className":94},[],[96],{"type":45,"value":97},"extensions\u002Fpython.md",{"type":45,"value":99},", ",{"type":40,"tag":76,"props":101,"children":103},{"className":102},[],[104],{"type":45,"value":105},"extensions\u002Ftypescript.md",{"type":45,"value":99},{"type":40,"tag":76,"props":108,"children":110},{"className":109},[],[111],{"type":45,"value":112},"extensions\u002Fgo.md",{"type":45,"value":114},").\nYou MUST read the relevant extension file before scoring assertions or\nanti-patterns, because assertion APIs and idiomatic patterns differ\nsignificantly across frameworks.",{"type":40,"tag":116,"props":117,"children":119},"h2",{"id":118},"why-a-per-test-grade",[120],{"type":45,"value":121},"Why a Per-Test Grade",{"type":40,"tag":48,"props":123,"children":124},{},[125,127,133,134,140,141,147,149,155],{"type":45,"value":126},"Suite-wide audits (",{"type":40,"tag":76,"props":128,"children":130},{"className":129},[],[131],{"type":45,"value":132},"test-anti-patterns",{"type":45,"value":99},{"type":40,"tag":76,"props":135,"children":137},{"className":136},[],[138],{"type":45,"value":139},"assertion-quality",{"type":45,"value":91},{"type":40,"tag":76,"props":142,"children":144},{"className":143},[],[145],{"type":45,"value":146},"test-smell-detection",{"type":45,"value":148},") produce excellent diagnostic reports, but they are\nhard to consume as a short PR comment. Reviewers of a PR mostly want to know:\n",{"type":40,"tag":150,"props":151,"children":152},"em",{},[153],{"type":45,"value":154},"for the tests this PR adds or changes, are they good?",{"type":45,"value":156}," This skill answers\nthat question with a one-row-per-test verdict that fits in a comment table.",{"type":40,"tag":116,"props":158,"children":160},{"id":159},"when-to-use",[161],{"type":45,"value":162},"When to Use",{"type":40,"tag":164,"props":165,"children":166},"ul",{},[167,173,178],{"type":40,"tag":168,"props":169,"children":170},"li",{},[171],{"type":45,"value":172},"A PR automation workflow needs to post a comment grading the tests\nintroduced or modified in a pull request.",{"type":40,"tag":168,"props":174,"children":175},{},[176],{"type":45,"value":177},"A reviewer has a specific list of tests (a file, a class, a method list,\nor a diff hunk) and wants a per-test verdict rather than a suite report.",{"type":40,"tag":168,"props":179,"children":180},{},[181],{"type":45,"value":182},"A maintainer wants to triage which of N tests in a contribution deserve\nfollow-up improvements.",{"type":40,"tag":116,"props":184,"children":186},{"id":185},"when-not-to-use",[187],{"type":45,"value":188},"When Not to Use",{"type":40,"tag":164,"props":190,"children":191},{},[192,219,247,268,273],{"type":40,"tag":168,"props":193,"children":194},{},[195,197,202,204,209,211,217],{"type":45,"value":196},"The caller wants a full suite audit or comparative metrics — use\n",{"type":40,"tag":76,"props":198,"children":200},{"className":199},[],[201],{"type":45,"value":132},{"type":45,"value":203}," (pragmatic) or ",{"type":40,"tag":76,"props":205,"children":207},{"className":206},[],[208],{"type":45,"value":146},{"type":45,"value":210}," (formal) and\nlet the ",{"type":40,"tag":76,"props":212,"children":214},{"className":213},[],[215],{"type":45,"value":216},"test-quality-auditor",{"type":45,"value":218}," agent orchestrate.",{"type":40,"tag":168,"props":220,"children":221},{},[222,224,229,231,237,239,245],{"type":45,"value":223},"The caller wants to ",{"type":40,"tag":150,"props":225,"children":226},{},[227],{"type":45,"value":228},"write",{"type":45,"value":230}," new tests — use ",{"type":40,"tag":76,"props":232,"children":234},{"className":233},[],[235],{"type":45,"value":236},"code-testing-generator",{"type":45,"value":238},"\n(any language) or ",{"type":40,"tag":76,"props":240,"children":242},{"className":241},[],[243],{"type":45,"value":244},"writing-mstest-tests",{"type":45,"value":246}," (MSTest specifically).",{"type":40,"tag":168,"props":248,"children":249},{},[250,252,258,260,266],{"type":45,"value":251},"The caller wants to measure code coverage or CRAP scores — use\n",{"type":40,"tag":76,"props":253,"children":255},{"className":254},[],[256],{"type":45,"value":257},"coverage-analysis",{"type":45,"value":259}," or ",{"type":40,"tag":76,"props":261,"children":263},{"className":262},[],[264],{"type":45,"value":265},"crap-score",{"type":45,"value":267}," (.NET only).",{"type":40,"tag":168,"props":269,"children":270},{},[271],{"type":45,"value":272},"The caller wants to fix issues directly in test code — invoke the\nappropriate editing skill.",{"type":40,"tag":168,"props":274,"children":275},{},[276,278,283],{"type":45,"value":277},"No specific list of tests is provided. Do ",{"type":40,"tag":54,"props":279,"children":280},{},[281],{"type":45,"value":282},"not",{"type":45,"value":284}," try to grade every test\nin the workspace; ask the caller for an explicit list or scope.",{"type":40,"tag":116,"props":286,"children":288},{"id":287},"inputs",[289],{"type":45,"value":290},"Inputs",{"type":40,"tag":292,"props":293,"children":294},"table",{},[295,319],{"type":40,"tag":296,"props":297,"children":298},"thead",{},[299],{"type":40,"tag":300,"props":301,"children":302},"tr",{},[303,309,314],{"type":40,"tag":304,"props":305,"children":306},"th",{},[307],{"type":45,"value":308},"Input",{"type":40,"tag":304,"props":310,"children":311},{},[312],{"type":45,"value":313},"Required",{"type":40,"tag":304,"props":315,"children":316},{},[317],{"type":45,"value":318},"Description",{"type":40,"tag":320,"props":321,"children":322},"tbody",{},[323,357,375,393],{"type":40,"tag":300,"props":324,"children":325},{},[326,332,337],{"type":40,"tag":327,"props":328,"children":329},"td",{},[330],{"type":45,"value":331},"Test methods",{"type":40,"tag":327,"props":333,"children":334},{},[335],{"type":45,"value":336},"Yes",{"type":40,"tag":327,"props":338,"children":339},{},[340,342,348,350,355],{"type":45,"value":341},"A scope to grade. Provide one of: (a) an explicit list of test method names (fully-qualified, e.g. ",{"type":40,"tag":76,"props":343,"children":345},{"className":344},[],[346],{"type":45,"value":347},"Namespace.ClassName.TestMethodName",{"type":45,"value":349},"); (b) one or more file paths plus an explicit instruction to grade every test declared in those files; or (c) a diff hunk \u002F PR identifier whose changed tests should be graded. File paths are recommended but optional when method names are unambiguous in the workspace. Ambiguous requests like ",{"type":40,"tag":150,"props":351,"children":352},{},[353],{"type":45,"value":354},"\"grade my tests\"",{"type":45,"value":356}," with no scope are rejected up-front (see Step 0); this skill is for curated input and does not auto-grade an entire workspace.",{"type":40,"tag":300,"props":358,"children":359},{},[360,365,370],{"type":40,"tag":327,"props":361,"children":362},{},[363],{"type":45,"value":364},"Test bodies \u002F spans",{"type":40,"tag":327,"props":366,"children":367},{},[368],{"type":45,"value":369},"Recommended",{"type":40,"tag":327,"props":371,"children":372},{},[373],{"type":45,"value":374},"The exact source lines for each test method. If omitted, read them from the listed files.",{"type":40,"tag":300,"props":376,"children":377},{},[378,383,388],{"type":40,"tag":327,"props":379,"children":380},{},[381],{"type":45,"value":382},"Production code",{"type":40,"tag":327,"props":384,"children":385},{},[386],{"type":45,"value":387},"No",{"type":40,"tag":327,"props":389,"children":390},{},[391],{"type":45,"value":392},"The code under test, for judging whether assertions cover the meaningful behaviors. When unavailable, mark relevant findings as \"Unverified\" rather than guessing.",{"type":40,"tag":300,"props":394,"children":395},{},[396,401,405],{"type":40,"tag":327,"props":397,"children":398},{},[399],{"type":45,"value":400},"Diff context",{"type":40,"tag":327,"props":402,"children":403},{},[404],{"type":45,"value":387},{"type":40,"tag":327,"props":406,"children":407},{},[408],{"type":45,"value":409},"When grading PR changes, the unified diff for each test method helps focus on what actually changed.",{"type":40,"tag":411,"props":412,"children":414},"h3",{"id":413},"step-0-validate-the-input",[415],{"type":45,"value":416},"Step 0: Validate the input",{"type":40,"tag":48,"props":418,"children":419},{},[420],{"type":45,"value":421},"Before doing anything else, check that the caller provided one of:",{"type":40,"tag":423,"props":424,"children":425},"ol",{},[426,436,453],{"type":40,"tag":168,"props":427,"children":428},{},[429,431],{"type":45,"value":430},"An explicit list of test method names, ",{"type":40,"tag":54,"props":432,"children":433},{},[434],{"type":45,"value":435},"or",{"type":40,"tag":168,"props":437,"children":438},{},[439,441,447,449],{"type":45,"value":440},"One or more file paths plus an explicit instruction to grade every test\ndeclared in those files (e.g., \"grade every test in ",{"type":40,"tag":76,"props":442,"children":444},{"className":443},[],[445],{"type":45,"value":446},"OrderTests.cs",{"type":45,"value":448},"\"), ",{"type":40,"tag":54,"props":450,"children":451},{},[452],{"type":45,"value":435},{"type":40,"tag":168,"props":454,"children":455},{},[456],{"type":45,"value":457},"A diff hunk or PR identifier whose changed tests should be graded.",{"type":40,"tag":48,"props":459,"children":460},{},[461,463,468,469,474,476,481,483,488,490,495,497,502],{"type":45,"value":462},"If the request is ambiguous (e.g., ",{"type":40,"tag":150,"props":464,"children":465},{},[466],{"type":45,"value":467},"\"Grade my tests\"",{"type":45,"value":99},{"type":40,"tag":150,"props":470,"children":471},{},[472],{"type":45,"value":473},"\"Are these tests\nany good?\"",{"type":45,"value":475}," with no scope, ",{"type":40,"tag":150,"props":477,"children":478},{},[479],{"type":45,"value":480},"\"Review the test suite\"",{"type":45,"value":482},"), ",{"type":40,"tag":54,"props":484,"children":485},{},[486],{"type":45,"value":487},"do not load\nextensions, do not read files, and do not grade anything",{"type":45,"value":489},". Reply with a\nshort message asking the caller to provide an explicit list \u002F file(s) \u002F\ndiff, and optionally point them at ",{"type":40,"tag":76,"props":491,"children":493},{"className":492},[],[494],{"type":45,"value":216},{"type":45,"value":496}," agent or\n",{"type":40,"tag":76,"props":498,"children":500},{"className":499},[],[501],{"type":45,"value":132},{"type":45,"value":503}," skill for full-suite analysis. Stop there.",{"type":40,"tag":116,"props":505,"children":507},{"id":506},"workflow",[508],{"type":45,"value":509},"Workflow",{"type":40,"tag":411,"props":511,"children":513},{"id":512},"step-1-detect-language-and-load-extension",[514],{"type":45,"value":515},"Step 1: Detect language and load extension",{"type":40,"tag":48,"props":517,"children":518},{},[519,521,526,528,533,535,540,542,547,549,554,556,561],{"type":45,"value":520},"Identify the target codebase's language and test framework from the file\nextensions and the test method markers in the provided list. Call the\n",{"type":40,"tag":76,"props":522,"children":524},{"className":523},[],[525],{"type":45,"value":81},{"type":45,"value":527}," skill and read the matching extension file (e.g.,\n",{"type":40,"tag":76,"props":529,"children":531},{"className":530},[],[532],{"type":45,"value":89},{"type":45,"value":534}," for MSTest\u002FxUnit\u002FNUnit\u002FTUnit, ",{"type":40,"tag":76,"props":536,"children":538},{"className":537},[],[539],{"type":45,"value":97},{"type":45,"value":541},"\nfor pytest, ",{"type":40,"tag":76,"props":543,"children":545},{"className":544},[],[546],{"type":45,"value":105},{"type":45,"value":548}," for Jest\u002FVitest, ",{"type":40,"tag":76,"props":550,"children":552},{"className":551},[],[553],{"type":45,"value":112},{"type":45,"value":555},"\nfor the standard ",{"type":40,"tag":76,"props":557,"children":559},{"className":558},[],[560],{"type":45,"value":21},{"type":45,"value":562}," package). If the input contains tests from\nmultiple languages, load each relevant extension and grade each test using\nits language's conventions.",{"type":40,"tag":411,"props":564,"children":566},{"id":565},"step-2-resolve-the-test-bodies",[567],{"type":45,"value":568},"Step 2: Resolve the test bodies",{"type":40,"tag":48,"props":570,"children":571},{},[572],{"type":45,"value":573},"For each entry in the input list:",{"type":40,"tag":423,"props":575,"children":576},{},[577,582,587],{"type":40,"tag":168,"props":578,"children":579},{},[580],{"type":45,"value":581},"If the test body is provided inline, use it directly.",{"type":40,"tag":168,"props":583,"children":584},{},[585],{"type":45,"value":586},"Otherwise read the file at the given path and locate the method by its\nfully-qualified name. Capture the full method body, including attributes\n\u002F decorators \u002F fixtures and any helper code that the test calls.",{"type":40,"tag":168,"props":588,"children":589},{},[590,592,598],{"type":45,"value":591},"If a method cannot be found, record it as ",{"type":40,"tag":76,"props":593,"children":595},{"className":594},[],[596],{"type":45,"value":597},"N\u002FA — method not found",{"type":45,"value":599}," and\ncontinue. Never invent a body to grade.",{"type":40,"tag":411,"props":601,"children":603},{"id":602},"step-3-score-each-test",[604],{"type":45,"value":605},"Step 3: Score each test",{"type":40,"tag":48,"props":607,"children":608},{},[609,611,616,618,623,625,629],{"type":45,"value":610},"Start every test at grade ",{"type":40,"tag":54,"props":612,"children":613},{},[614],{"type":45,"value":615},"A (score band 90–100)",{"type":45,"value":617},", then apply deductions\nstrictly for ",{"type":40,"tag":54,"props":619,"children":620},{},[621],{"type":45,"value":622},"observable issues",{"type":45,"value":624}," in the captured body. Do ",{"type":40,"tag":54,"props":626,"children":627},{},[628],{"type":45,"value":282},{"type":45,"value":630}," deduct\nfor hypothetical concerns (e.g., \"could have more negative assertions\")\nunless the production code clearly demands them and the production code is\navailable.",{"type":40,"tag":48,"props":632,"children":633},{},[634,636,642],{"type":45,"value":635},"When production code is unavailable, grade observable issues in the test body\nnormally, but do not infer missing behaviors or deduct for them. State\n",{"type":40,"tag":76,"props":637,"children":639},{"className":638},[],[640],{"type":45,"value":641},"Production-dependent behavior coverage: Unverified",{"type":45,"value":643}," once in the summary so the\nreader can distinguish test-body findings from claims that require source code.",{"type":40,"tag":645,"props":646,"children":648},"h4",{"id":647},"three-sub-dimensions",[649],{"type":45,"value":650},"Three sub-dimensions",{"type":40,"tag":48,"props":652,"children":653},{},[654],{"type":45,"value":655},"Compute three sub-grades (each A–F) that together drive the overall grade.",{"type":40,"tag":657,"props":658,"children":660},"h5",{"id":659},"a-assertion-strength",[661],{"type":45,"value":662},"A. Assertion strength",{"type":40,"tag":48,"props":664,"children":665},{},[666],{"type":45,"value":667},"Read the loaded language extension's assertion API list and classify every\nassertion in the test body. Score from highest to lowest:",{"type":40,"tag":292,"props":669,"children":670},{},[671,687],{"type":40,"tag":296,"props":672,"children":673},{},[674],{"type":40,"tag":300,"props":675,"children":676},{},[677,682],{"type":40,"tag":304,"props":678,"children":679},{},[680],{"type":45,"value":681},"Sub-grade",{"type":40,"tag":304,"props":683,"children":684},{},[685],{"type":45,"value":686},"Pattern",{"type":40,"tag":320,"props":688,"children":689},{},[690,752,768,807,846],{"type":40,"tag":300,"props":691,"children":692},{},[693,701],{"type":40,"tag":327,"props":694,"children":695},{},[696],{"type":40,"tag":54,"props":697,"children":698},{},[699],{"type":45,"value":700},"A",{"type":40,"tag":327,"props":702,"children":703},{},[704,706,712,713,719,720,726,728,734,736,742,744,750],{"type":45,"value":705},"At least one meaningful value assertion (equality \u002F structural \u002F exception \u002F state) plus, where appropriate, additional checks (negative, type, collection contents). Mock-call verifications (",{"type":40,"tag":76,"props":707,"children":709},{"className":708},[],[710],{"type":45,"value":711},"Verify",{"type":45,"value":99},{"type":40,"tag":76,"props":714,"children":716},{"className":715},[],[717],{"type":45,"value":718},"toHaveBeenCalledWith",{"type":45,"value":99},{"type":40,"tag":76,"props":721,"children":723},{"className":722},[],[724],{"type":45,"value":725},"Should -Invoke",{"type":45,"value":727},") and bare assertion forms (pytest ",{"type":40,"tag":76,"props":729,"children":731},{"className":730},[],[732],{"type":45,"value":733},"assert",{"type":45,"value":735},", Go ",{"type":40,"tag":76,"props":737,"children":739},{"className":738},[],[740],{"type":45,"value":741},"if got != want { t.Errorf(...) }",{"type":45,"value":743},", Rust ",{"type":40,"tag":76,"props":745,"children":747},{"className":746},[],[748],{"type":45,"value":749},"assert!()",{"type":45,"value":751},") count as real assertions.",{"type":40,"tag":300,"props":753,"children":754},{},[755,763],{"type":40,"tag":327,"props":756,"children":757},{},[758],{"type":40,"tag":54,"props":759,"children":760},{},[761],{"type":45,"value":762},"B",{"type":40,"tag":327,"props":764,"children":765},{},[766],{"type":45,"value":767},"One clear meaningful assertion that verifies the behavior under test.",{"type":40,"tag":300,"props":769,"children":770},{},[771,779],{"type":40,"tag":327,"props":772,"children":773},{},[774],{"type":40,"tag":54,"props":775,"children":776},{},[777],{"type":45,"value":778},"C",{"type":40,"tag":327,"props":780,"children":781},{},[782,784,790,792,798,799,805],{"type":45,"value":783},"Only trivial assertions (single ",{"type":40,"tag":76,"props":785,"children":787},{"className":786},[],[788],{"type":45,"value":789},"IsNotNull",{"type":45,"value":791}," \u002F ",{"type":40,"tag":76,"props":793,"children":795},{"className":794},[],[796],{"type":45,"value":797},"toBeDefined",{"type":45,"value":791},{"type":40,"tag":76,"props":800,"children":802},{"className":801},[],[803],{"type":45,"value":804},"assert x is not None",{"type":45,"value":806},"), or assertions that check a single field while the operation produces a richer result.",{"type":40,"tag":300,"props":808,"children":809},{},[810,818],{"type":40,"tag":327,"props":811,"children":812},{},[813],{"type":40,"tag":54,"props":814,"children":815},{},[816],{"type":45,"value":817},"D",{"type":40,"tag":327,"props":819,"children":820},{},[821,823,829,830,836,838,844],{"type":45,"value":822},"One self-referential \u002F tautological assertion (",{"type":40,"tag":76,"props":824,"children":826},{"className":825},[],[827],{"type":45,"value":828},"Assert.AreEqual(x, x)",{"type":45,"value":99},{"type":40,"tag":76,"props":831,"children":833},{"className":832},[],[834],{"type":45,"value":835},"assert dto.name == dto.name",{"type":45,"value":837},", round-trip identity without a non-trivial input), or broad exception assertions (",{"type":40,"tag":76,"props":839,"children":841},{"className":840},[],[842],{"type":45,"value":843},"Assert.ThrowsException\u003CException>",{"type":45,"value":845},").",{"type":40,"tag":300,"props":847,"children":848},{},[849,857],{"type":40,"tag":327,"props":850,"children":851},{},[852],{"type":40,"tag":54,"props":853,"children":854},{},[855],{"type":45,"value":856},"F",{"type":40,"tag":327,"props":858,"children":859},{},[860,862,867,869,875,876,882,883,889,891,897,899,905,907,913,915,921,922,927],{"type":45,"value":861},"No assertions at all; ",{"type":40,"tag":54,"props":863,"children":864},{},[865],{"type":45,"value":866},"all",{"type":45,"value":868}," assertions are always-true literals (",{"type":40,"tag":76,"props":870,"children":872},{"className":871},[],[873],{"type":45,"value":874},"Assert.IsTrue(true)",{"type":45,"value":99},{"type":40,"tag":76,"props":877,"children":879},{"className":878},[],[880],{"type":45,"value":881},"assert True",{"type":45,"value":99},{"type":40,"tag":76,"props":884,"children":886},{"className":885},[],[887],{"type":45,"value":888},"expect(true).toBe(true)",{"type":45,"value":890},") — these verify nothing and are equivalent to having no assertions; or all assertions are silently un-awaited (e.g., ",{"type":40,"tag":76,"props":892,"children":894},{"className":893},[],[895],{"type":45,"value":896},"expect(promise).resolves.toBe(x)",{"type":45,"value":898}," without ",{"type":40,"tag":76,"props":900,"children":902},{"className":901},[],[903],{"type":45,"value":904},"await",{"type":45,"value":906},"\u002F",{"type":40,"tag":76,"props":908,"children":910},{"className":909},[],[911],{"type":45,"value":912},"return",{"type":45,"value":914},", async TUnit\u002FxUnit ",{"type":40,"tag":76,"props":916,"children":918},{"className":917},[],[919],{"type":45,"value":920},"Assert.ThrowsAsync",{"type":45,"value":898},{"type":40,"tag":76,"props":923,"children":925},{"className":924},[],[926],{"type":45,"value":904},{"type":45,"value":928},", pytest-asyncio with un-awaited coroutine).",{"type":40,"tag":48,"props":930,"children":931},{},[932,934,940,942,948,949,955,956,962,963,969,970,976,977,983,985,989,991,997,999,1003],{"type":45,"value":933},"Exception and error-path tests (",{"type":40,"tag":76,"props":935,"children":937},{"className":936},[],[938],{"type":45,"value":939},"Assert.ThrowsException\u003CT>",{"type":45,"value":941},", constrained\n",{"type":40,"tag":76,"props":943,"children":945},{"className":944},[],[946],{"type":45,"value":947},"pytest.raises",{"type":45,"value":99},{"type":40,"tag":76,"props":950,"children":952},{"className":951},[],[953],{"type":45,"value":954},"expect(fn).toThrow",{"type":45,"value":99},{"type":40,"tag":76,"props":957,"children":959},{"className":958},[],[960],{"type":45,"value":961},"assertThrows",{"type":45,"value":99},{"type":40,"tag":76,"props":964,"children":966},{"className":965},[],[967],{"type":45,"value":968},"#[should_panic]",{"type":45,"value":91},{"type":40,"tag":76,"props":971,"children":973},{"className":972},[],[974],{"type":45,"value":975},"Should -Throw",{"type":45,"value":99},{"type":40,"tag":76,"props":978,"children":980},{"className":979},[],[981],{"type":45,"value":982},"EXPECT_THROW",{"type":45,"value":984},", or Go code that verifies an expected non-nil\nerror) are complete on their own. Give Assertion strength ",{"type":40,"tag":54,"props":986,"children":987},{},[988],{"type":45,"value":700},{"type":45,"value":990}," when the test\nchecks the exact promised error condition for its stated scope. Do not deduct\nfor having only that assertion, and do not require an error-message assertion\nunless the message is part of the documented contract. A Go happy-path test\nthat only checks ",{"type":40,"tag":76,"props":992,"children":994},{"className":993},[],[995],{"type":45,"value":996},"err == nil",{"type":45,"value":998}," while discarding a meaningful returned value is\nstill ",{"type":40,"tag":54,"props":1000,"children":1001},{},[1002],{"type":45,"value":778},{"type":45,"value":1004}," because it does not verify the successful result.",{"type":40,"tag":657,"props":1006,"children":1008},{"id":1007},"b-structure-focus",[1009],{"type":45,"value":1010},"B. Structure & focus",{"type":40,"tag":292,"props":1012,"children":1013},{},[1014,1028],{"type":40,"tag":296,"props":1015,"children":1016},{},[1017],{"type":40,"tag":300,"props":1018,"children":1019},{},[1020,1024],{"type":40,"tag":304,"props":1021,"children":1022},{},[1023],{"type":45,"value":681},{"type":40,"tag":304,"props":1025,"children":1026},{},[1027],{"type":45,"value":686},{"type":40,"tag":320,"props":1029,"children":1030},{},[1031,1046,1061,1076,1106],{"type":40,"tag":300,"props":1032,"children":1033},{},[1034,1041],{"type":40,"tag":327,"props":1035,"children":1036},{},[1037],{"type":40,"tag":54,"props":1038,"children":1039},{},[1040],{"type":45,"value":700},{"type":40,"tag":327,"props":1042,"children":1043},{},[1044],{"type":45,"value":1045},"Clear Arrange-Act-Assert (or Given-When-Then) separation. Single behavior under test. Body under ~30 lines. Setup uses framework conventions.",{"type":40,"tag":300,"props":1047,"children":1048},{},[1049,1056],{"type":40,"tag":327,"props":1050,"children":1051},{},[1052],{"type":40,"tag":54,"props":1053,"children":1054},{},[1055],{"type":45,"value":762},{"type":40,"tag":327,"props":1057,"children":1058},{},[1059],{"type":45,"value":1060},"One mild structural issue (slightly long body, missing blank lines between phases) but intent is clear.",{"type":40,"tag":300,"props":1062,"children":1063},{},[1064,1071],{"type":40,"tag":327,"props":1065,"children":1066},{},[1067],{"type":40,"tag":54,"props":1068,"children":1069},{},[1070],{"type":45,"value":778},{"type":40,"tag":327,"props":1072,"children":1073},{},[1074],{"type":45,"value":1075},"Multiple behaviors mixed in one test, or AAA phases interleaved enough to slow comprehension.",{"type":40,"tag":300,"props":1077,"children":1078},{},[1079,1086],{"type":40,"tag":327,"props":1080,"children":1081},{},[1082],{"type":40,"tag":54,"props":1083,"children":1084},{},[1085],{"type":45,"value":817},{"type":40,"tag":327,"props":1087,"children":1088},{},[1089,1091,1097,1098,1104],{"type":45,"value":1090},"Conditional logic in the test (",{"type":40,"tag":76,"props":1092,"children":1094},{"className":1093},[],[1095],{"type":45,"value":1096},"if",{"type":45,"value":906},{"type":40,"tag":76,"props":1099,"children":1101},{"className":1100},[],[1102],{"type":45,"value":1103},"switch",{"type":45,"value":1105}," driving assertions) — except for idiomatic Go\u002FRust table-driven sub-test loops; or test relies on previous test state (ordering dependency).",{"type":40,"tag":300,"props":1107,"children":1108},{},[1109,1116],{"type":40,"tag":327,"props":1110,"children":1111},{},[1112],{"type":40,"tag":54,"props":1113,"children":1114},{},[1115],{"type":45,"value":856},{"type":40,"tag":327,"props":1117,"children":1118},{},[1119],{"type":45,"value":1120},"Test exceeds ~60 lines and verifies multiple unrelated behaviors; or shares mutable state with other tests through statics\u002Fglobals without reset.",{"type":40,"tag":657,"props":1122,"children":1124},{"id":1123},"c-anti-pattern-hygiene",[1125],{"type":45,"value":1126},"C. Anti-pattern hygiene",{"type":40,"tag":48,"props":1128,"children":1129},{},[1130],{"type":45,"value":1131},"Scan against the catalog below. The Anti-pattern sub-grade is computed\nin two passes and combined deterministically:",{"type":40,"tag":423,"props":1133,"children":1134},{},[1135,1165],{"type":40,"tag":168,"props":1136,"children":1137},{},[1138,1143,1145,1150,1151,1156,1158,1163],{"type":40,"tag":54,"props":1139,"children":1140},{},[1141],{"type":45,"value":1142},"Hard ceiling pass.",{"type":45,"value":1144}," Every ",{"type":40,"tag":54,"props":1146,"children":1147},{},[1148],{"type":45,"value":1149},"Critical",{"type":45,"value":259},{"type":40,"tag":54,"props":1152,"children":1153},{},[1154],{"type":45,"value":1155},"High",{"type":45,"value":1157}," finding sets a\nmaximum sub-grade (F, D, or C as labeled). Take the ",{"type":40,"tag":54,"props":1159,"children":1160},{},[1161],{"type":45,"value":1162},"worst",{"type":45,"value":1164}," ceiling\nacross all matched Critical\u002FHigh findings — these do not accumulate\n(a single F finding caps the sub-grade at F regardless of how many\nother Critical\u002FHigh findings are present).",{"type":40,"tag":168,"props":1166,"children":1167},{},[1168,1173,1175,1179,1181,1186],{"type":40,"tag":54,"props":1169,"children":1170},{},[1171],{"type":45,"value":1172},"Medium-deduction pass.",{"type":45,"value":1174}," Start from ",{"type":40,"tag":54,"props":1176,"children":1177},{},[1178],{"type":45,"value":700},{"type":45,"value":1180},", then for each ",{"type":40,"tag":54,"props":1182,"children":1183},{},[1184],{"type":45,"value":1185},"Medium",{"type":45,"value":1187},"\nfinding deduct one sub-grade level (A→B, B→C, C→D, D→F). These do\naccumulate across findings.",{"type":40,"tag":48,"props":1189,"children":1190},{},[1191,1193,1198,1200,1206,1208,1213],{"type":45,"value":1192},"The final Anti-pattern sub-grade is the ",{"type":40,"tag":54,"props":1194,"children":1195},{},[1196],{"type":45,"value":1197},"worse",{"type":45,"value":1199}," of the two passes\n(i.e., ",{"type":40,"tag":76,"props":1201,"children":1203},{"className":1202},[],[1204],{"type":45,"value":1205},"min(hard_ceiling, A − medium_count)",{"type":45,"value":1207},"). ",{"type":40,"tag":54,"props":1209,"children":1210},{},[1211],{"type":45,"value":1212},"Low",{"type":45,"value":1214}," findings never\naffect the grade — mention them in the note only.",{"type":40,"tag":48,"props":1216,"children":1217},{},[1218],{"type":45,"value":1219},"Examples (Critical\u002FHigh and Medium counts → Anti-pattern sub-grade):",{"type":40,"tag":164,"props":1221,"children":1222},{},[1223,1234,1245,1254,1279],{"type":40,"tag":168,"props":1224,"children":1225},{},[1226,1228,1232],{"type":45,"value":1227},"Zero Critical\u002FHigh, 1 Medium → ",{"type":40,"tag":54,"props":1229,"children":1230},{},[1231],{"type":45,"value":762},{"type":45,"value":1233}," (A − 1)",{"type":40,"tag":168,"props":1235,"children":1236},{},[1237,1239,1243],{"type":45,"value":1238},"Zero Critical\u002FHigh, 3 Medium → ",{"type":40,"tag":54,"props":1240,"children":1241},{},[1242],{"type":45,"value":817},{"type":45,"value":1244}," (A − 3)",{"type":40,"tag":168,"props":1246,"children":1247},{},[1248,1250],{"type":45,"value":1249},"One C-ceiling (e.g., over-mocking), 0 Medium → ",{"type":40,"tag":54,"props":1251,"children":1252},{},[1253],{"type":45,"value":778},{"type":40,"tag":168,"props":1255,"children":1256},{},[1257,1259,1263,1265,1271,1273,1277],{"type":45,"value":1258},"One C-ceiling, 2 Medium → ",{"type":40,"tag":54,"props":1260,"children":1261},{},[1262],{"type":45,"value":817},{"type":45,"value":1264}," (",{"type":40,"tag":76,"props":1266,"children":1268},{"className":1267},[],[1269],{"type":45,"value":1270},"min(C, A − 2 = C) = C",{"type":45,"value":1272},", but a third Medium would tip to ",{"type":40,"tag":54,"props":1274,"children":1275},{},[1276],{"type":45,"value":817},{"type":45,"value":1278},")",{"type":40,"tag":168,"props":1280,"children":1281},{},[1282,1284],{"type":45,"value":1283},"One F-finding (e.g., swallowed exception) plus any number of Medium → ",{"type":40,"tag":54,"props":1285,"children":1286},{},[1287],{"type":45,"value":856},{"type":40,"tag":48,"props":1289,"children":1290},{},[1291],{"type":40,"tag":54,"props":1292,"children":1293},{},[1294],{"type":45,"value":1295},"Critical (drop straight to F or D)",{"type":40,"tag":164,"props":1297,"children":1298},{},[1299,1304,1357,1378,1408,1425],{"type":40,"tag":168,"props":1300,"children":1301},{},[1302],{"type":45,"value":1303},"No assertions at all → F (also drives Assertion sub-grade to F)",{"type":40,"tag":168,"props":1305,"children":1306},{},[1307,1309,1315,1317,1323,1325,1331,1333,1339,1341,1347,1349,1355],{"type":45,"value":1308},"Swallowed exceptions: ",{"type":40,"tag":76,"props":1310,"children":1312},{"className":1311},[],[1313],{"type":45,"value":1314},"try { … } catch { }",{"type":45,"value":1316}," (.NET), bare ",{"type":40,"tag":76,"props":1318,"children":1320},{"className":1319},[],[1321],{"type":45,"value":1322},"except: pass",{"type":45,"value":1324},"\n(Python), ",{"type":40,"tag":76,"props":1326,"children":1328},{"className":1327},[],[1329],{"type":45,"value":1330},"try { … } catch (e) {}",{"type":45,"value":1332}," (JS\u002FTS\u002FJava), ",{"type":40,"tag":76,"props":1334,"children":1336},{"className":1335},[],[1337],{"type":45,"value":1338},"defer recover()",{"type":45,"value":1340},"\nwithout re-panic (Go), ",{"type":40,"tag":76,"props":1342,"children":1344},{"className":1343},[],[1345],{"type":45,"value":1346},"rescue StandardError",{"type":45,"value":1348}," with no assertion (Ruby),\nempty ",{"type":40,"tag":76,"props":1350,"children":1352},{"className":1351},[],[1353],{"type":45,"value":1354},"catch",{"type":45,"value":1356}," (Kotlin\u002FSwift) → F",{"type":40,"tag":168,"props":1358,"children":1359},{},[1360,1362,1368,1370,1376],{"type":45,"value":1361},"Assert-in-catch pattern (",{"type":40,"tag":76,"props":1363,"children":1365},{"className":1364},[],[1366],{"type":45,"value":1367},"Assert.Fail(ex.Message)",{"type":45,"value":1369}," instead of\n",{"type":40,"tag":76,"props":1371,"children":1373},{"className":1372},[],[1374],{"type":45,"value":1375},"Assert.ThrowsException",{"type":45,"value":1377},") → D",{"type":40,"tag":168,"props":1379,"children":1380},{},[1381,1383,1388,1389,1394,1395,1400,1402,1406],{"type":45,"value":1382},"Always-true literal assertions (",{"type":40,"tag":76,"props":1384,"children":1386},{"className":1385},[],[1387],{"type":45,"value":874},{"type":45,"value":99},{"type":40,"tag":76,"props":1390,"children":1392},{"className":1391},[],[1393],{"type":45,"value":881},{"type":45,"value":91},{"type":40,"tag":76,"props":1396,"children":1398},{"className":1397},[],[1399],{"type":45,"value":888},{"type":45,"value":1401},") → ",{"type":40,"tag":54,"props":1403,"children":1404},{},[1405],{"type":45,"value":856},{"type":45,"value":1407}," (verifies nothing; also drives\nAssertion sub-grade to F)",{"type":40,"tag":168,"props":1409,"children":1410},{},[1411,1413,1418,1419,1424],{"type":45,"value":1412},"Self-referential \u002F tautological assertions on bound values\n(",{"type":40,"tag":76,"props":1414,"children":1416},{"className":1415},[],[1417],{"type":45,"value":828},{"type":45,"value":99},{"type":40,"tag":76,"props":1420,"children":1422},{"className":1421},[],[1423],{"type":45,"value":835},{"type":45,"value":1377},{"type":40,"tag":168,"props":1426,"children":1427},{},[1428],{"type":45,"value":1429},"Commented-out assertions → D",{"type":40,"tag":48,"props":1431,"children":1432},{},[1433],{"type":40,"tag":54,"props":1434,"children":1435},{},[1436],{"type":45,"value":1437},"High (drop one or two sub-grades)",{"type":40,"tag":164,"props":1439,"children":1440},{},[1441,1518,1586,1606,1611,1672,1677],{"type":40,"tag":168,"props":1442,"children":1443},{},[1444,1446,1452,1453,1459,1460,1466,1467,1473,1475,1481,1482,1488,1489,1495,1496,1502,1503,1509,1510,1516],{"type":45,"value":1445},"Wall-clock sleep used for synchronization: ",{"type":40,"tag":76,"props":1447,"children":1449},{"className":1448},[],[1450],{"type":45,"value":1451},"Thread.Sleep",{"type":45,"value":99},{"type":40,"tag":76,"props":1454,"children":1456},{"className":1455},[],[1457],{"type":45,"value":1458},"Task.Delay",{"type":45,"value":91},{"type":40,"tag":76,"props":1461,"children":1463},{"className":1462},[],[1464],{"type":45,"value":1465},"time.sleep",{"type":45,"value":99},{"type":40,"tag":76,"props":1468,"children":1470},{"className":1469},[],[1471],{"type":45,"value":1472},"setTimeout",{"type":45,"value":1474},"-based wait, ",{"type":40,"tag":76,"props":1476,"children":1478},{"className":1477},[],[1479],{"type":45,"value":1480},"Thread.sleep",{"type":45,"value":99},{"type":40,"tag":76,"props":1483,"children":1485},{"className":1484},[],[1486],{"type":45,"value":1487},"time.Sleep",{"type":45,"value":91},{"type":40,"tag":76,"props":1490,"children":1492},{"className":1491},[],[1493],{"type":45,"value":1494},"sleep",{"type":45,"value":99},{"type":40,"tag":76,"props":1497,"children":1499},{"className":1498},[],[1500],{"type":45,"value":1501},"std::thread::sleep",{"type":45,"value":99},{"type":40,"tag":76,"props":1504,"children":1506},{"className":1505},[],[1507],{"type":45,"value":1508},"Start-Sleep",{"type":45,"value":91},{"type":40,"tag":76,"props":1511,"children":1513},{"className":1512},[],[1514],{"type":45,"value":1515},"std::this_thread::sleep_for",{"type":45,"value":1517}," (in a unit test) → D",{"type":40,"tag":168,"props":1519,"children":1520},{},[1521,1523,1529,1530,1536,1537,1543,1544,1550,1551,1557,1558,1564,1565,1571,1572,1578,1579,1585],{"type":45,"value":1522},"Unseeded randomness, wall-clock reads without abstraction\n(",{"type":40,"tag":76,"props":1524,"children":1526},{"className":1525},[],[1527],{"type":45,"value":1528},"DateTime.Now",{"type":45,"value":99},{"type":40,"tag":76,"props":1531,"children":1533},{"className":1532},[],[1534],{"type":45,"value":1535},"datetime.now()",{"type":45,"value":99},{"type":40,"tag":76,"props":1538,"children":1540},{"className":1539},[],[1541],{"type":45,"value":1542},"Date.now()",{"type":45,"value":91},{"type":40,"tag":76,"props":1545,"children":1547},{"className":1546},[],[1548],{"type":45,"value":1549},"System.currentTimeMillis()",{"type":45,"value":99},{"type":40,"tag":76,"props":1552,"children":1554},{"className":1553},[],[1555],{"type":45,"value":1556},"time.Now()",{"type":45,"value":99},{"type":40,"tag":76,"props":1559,"children":1561},{"className":1560},[],[1562],{"type":45,"value":1563},"Time.now",{"type":45,"value":91},{"type":40,"tag":76,"props":1566,"children":1568},{"className":1567},[],[1569],{"type":45,"value":1570},"Instant::now()",{"type":45,"value":99},{"type":40,"tag":76,"props":1573,"children":1575},{"className":1574},[],[1576],{"type":45,"value":1577},"Get-Date",{"type":45,"value":99},{"type":40,"tag":76,"props":1580,"children":1582},{"className":1581},[],[1583],{"type":45,"value":1584},"system_clock::now",{"type":45,"value":1377},{"type":40,"tag":168,"props":1587,"children":1588},{},[1589,1591,1597,1598,1604],{"type":45,"value":1590},"Hard-coded environment-dependent paths (",{"type":40,"tag":76,"props":1592,"children":1594},{"className":1593},[],[1595],{"type":45,"value":1596},"C:\\…",{"type":45,"value":99},{"type":40,"tag":76,"props":1599,"children":1601},{"className":1600},[],[1602],{"type":45,"value":1603},"\u002Ftmp\u002F…",{"type":45,"value":1605},", network hosts) → D",{"type":40,"tag":168,"props":1607,"children":1608},{},[1609],{"type":45,"value":1610},"Ordering dependency on mutable static \u002F package globals → D",{"type":40,"tag":168,"props":1612,"children":1613},{},[1614,1616,1621,1622,1628,1629,1635,1637,1642,1643,1649,1650,1655,1657,1663,1664,1670],{"type":45,"value":1615},"Broad exception assertion (",{"type":40,"tag":76,"props":1617,"children":1619},{"className":1618},[],[1620],{"type":45,"value":843},{"type":45,"value":91},{"type":40,"tag":76,"props":1623,"children":1625},{"className":1624},[],[1626],{"type":45,"value":1627},"pytest.raises(Exception)",{"type":45,"value":99},{"type":40,"tag":76,"props":1630,"children":1632},{"className":1631},[],[1633],{"type":45,"value":1634},"expect(fn).toThrow(Error)",{"type":45,"value":1636}," without matcher,\n",{"type":40,"tag":76,"props":1638,"children":1640},{"className":1639},[],[1641],{"type":45,"value":968},{"type":45,"value":898},{"type":40,"tag":76,"props":1644,"children":1646},{"className":1645},[],[1647],{"type":45,"value":1648},"expected = \"…\"",{"type":45,"value":99},{"type":40,"tag":76,"props":1651,"children":1653},{"className":1652},[],[1654],{"type":45,"value":975},{"type":45,"value":1656}," without\n",{"type":40,"tag":76,"props":1658,"children":1660},{"className":1659},[],[1661],{"type":45,"value":1662},"-ExpectedMessage",{"type":45,"value":99},{"type":40,"tag":76,"props":1665,"children":1667},{"className":1666},[],[1668],{"type":45,"value":1669},"EXPECT_ANY_THROW",{"type":45,"value":1671},") → C",{"type":40,"tag":168,"props":1673,"children":1674},{},[1675],{"type":45,"value":1676},"Over-mocking: more mock setup lines than test logic, or verifying exact\ncall sequences instead of outcomes → C",{"type":40,"tag":168,"props":1678,"children":1679},{},[1680],{"type":45,"value":1681},"Implementation coupling: reflection on private members, casting to\ninternal types to access state → C",{"type":40,"tag":48,"props":1683,"children":1684},{},[1685],{"type":40,"tag":54,"props":1686,"children":1687},{},[1688],{"type":45,"value":1689},"Medium (drop one sub-grade)",{"type":40,"tag":164,"props":1691,"children":1692},{},[1693,1720,1747,1752,1757],{"type":40,"tag":168,"props":1694,"children":1695},{},[1696,1698,1704,1705,1711,1712,1718],{"type":45,"value":1697},"Poor name: ",{"type":40,"tag":76,"props":1699,"children":1701},{"className":1700},[],[1702],{"type":45,"value":1703},"Test1",{"type":45,"value":99},{"type":40,"tag":76,"props":1706,"children":1708},{"className":1707},[],[1709],{"type":45,"value":1710},"TestMethod",{"type":45,"value":99},{"type":40,"tag":76,"props":1713,"children":1715},{"className":1714},[],[1716],{"type":45,"value":1717},"test",{"type":45,"value":1719},", single-word name that says\nnothing about scenario or expected outcome (judge against the language\nextension's convention) → drop one sub-grade",{"type":40,"tag":168,"props":1721,"children":1722},{},[1723,1725,1731,1732,1738,1739,1745],{"type":45,"value":1724},"Magic values: unexplained ",{"type":40,"tag":76,"props":1726,"children":1728},{"className":1727},[],[1729],{"type":45,"value":1730},"42",{"type":45,"value":99},{"type":40,"tag":76,"props":1733,"children":1735},{"className":1734},[],[1736],{"type":45,"value":1737},"\"foo\"",{"type":45,"value":99},{"type":40,"tag":76,"props":1740,"children":1742},{"className":1741},[],[1743],{"type":45,"value":1744},"0x1234",{"type":45,"value":1746}," in arrange\u002Fassert\nwithout naming or comment → drop one sub-grade",{"type":40,"tag":168,"props":1748,"children":1749},{},[1750],{"type":45,"value":1751},"Giant test (>30 lines covering a single behavior) → drop one sub-grade",{"type":40,"tag":168,"props":1753,"children":1754},{},[1755],{"type":45,"value":1756},"Assertion messages that just repeat the assertion text → drop one sub-grade",{"type":40,"tag":168,"props":1758,"children":1759},{},[1760],{"type":45,"value":1761},"Missing AAA \u002F GWT separation when the test is non-trivial → drop one sub-grade",{"type":40,"tag":48,"props":1763,"children":1764},{},[1765],{"type":40,"tag":54,"props":1766,"children":1767},{},[1768],{"type":45,"value":1769},"Low (note only, no deduction)",{"type":40,"tag":164,"props":1771,"children":1772},{},[1773],{"type":40,"tag":168,"props":1774,"children":1775},{},[1776,1778,1784,1785,1791,1792,1798,1799,1805,1806,1812,1813,1819,1820,1826,1827,1833,1834,1840],{"type":45,"value":1777},"Unused setup\u002Fteardown hooks; print debugging left in (",{"type":40,"tag":76,"props":1779,"children":1781},{"className":1780},[],[1782],{"type":45,"value":1783},"Console.WriteLine",{"type":45,"value":91},{"type":40,"tag":76,"props":1786,"children":1788},{"className":1787},[],[1789],{"type":45,"value":1790},"print",{"type":45,"value":99},{"type":40,"tag":76,"props":1793,"children":1795},{"className":1794},[],[1796],{"type":45,"value":1797},"console.log",{"type":45,"value":99},{"type":40,"tag":76,"props":1800,"children":1802},{"className":1801},[],[1803],{"type":45,"value":1804},"System.out.println",{"type":45,"value":99},{"type":40,"tag":76,"props":1807,"children":1809},{"className":1808},[],[1810],{"type":45,"value":1811},"fmt.Println",{"type":45,"value":99},{"type":40,"tag":76,"props":1814,"children":1816},{"className":1815},[],[1817],{"type":45,"value":1818},"puts",{"type":45,"value":91},{"type":40,"tag":76,"props":1821,"children":1823},{"className":1822},[],[1824],{"type":45,"value":1825},"dbg!",{"type":45,"value":99},{"type":40,"tag":76,"props":1828,"children":1830},{"className":1829},[],[1831],{"type":45,"value":1832},"Write-Host",{"type":45,"value":99},{"type":40,"tag":76,"props":1835,"children":1837},{"className":1836},[],[1838],{"type":45,"value":1839},"std::cout",{"type":45,"value":1841},"); inconsistent naming versus siblings;\nleftover TODO comments. Mention in the note column but do not deduct.",{"type":40,"tag":645,"props":1843,"children":1845},{"id":1844},"combining-sub-grades",[1846],{"type":45,"value":1847},"Combining sub-grades",{"type":40,"tag":48,"props":1849,"children":1850},{},[1851],{"type":45,"value":1852},"Convert sub-grades to numeric points: A=4, B=3, C=2, D=1, F=0.",{"type":40,"tag":164,"props":1854,"children":1855},{},[1856,1872,1935],{"type":40,"tag":168,"props":1857,"children":1858},{},[1859,1864,1866],{"type":40,"tag":54,"props":1860,"children":1861},{},[1862],{"type":45,"value":1863},"Overall score band",{"type":45,"value":1865}," = weighted average:\n",{"type":40,"tag":76,"props":1867,"children":1869},{"className":1868},[],[1870],{"type":45,"value":1871},"0.45 × Assertion + 0.30 × Anti-pattern + 0.25 × Structure",{"type":40,"tag":168,"props":1873,"children":1874},{},[1875,1877],{"type":45,"value":1876},"Map to letter:\n",{"type":40,"tag":164,"props":1878,"children":1879},{},[1880,1891,1902,1913,1924],{"type":40,"tag":168,"props":1881,"children":1882},{},[1883,1885,1889],{"type":45,"value":1884},"≥ 3.5 → ",{"type":40,"tag":54,"props":1886,"children":1887},{},[1888],{"type":45,"value":700},{"type":45,"value":1890}," (band 90–100)",{"type":40,"tag":168,"props":1892,"children":1893},{},[1894,1896,1900],{"type":45,"value":1895},"≥ 2.8 → ",{"type":40,"tag":54,"props":1897,"children":1898},{},[1899],{"type":45,"value":762},{"type":45,"value":1901}," (band 80–89)",{"type":40,"tag":168,"props":1903,"children":1904},{},[1905,1907,1911],{"type":45,"value":1906},"≥ 2.0 → ",{"type":40,"tag":54,"props":1908,"children":1909},{},[1910],{"type":45,"value":778},{"type":45,"value":1912}," (band 70–79)",{"type":40,"tag":168,"props":1914,"children":1915},{},[1916,1918,1922],{"type":45,"value":1917},"≥ 1.2 → ",{"type":40,"tag":54,"props":1919,"children":1920},{},[1921],{"type":45,"value":817},{"type":45,"value":1923}," (band 60–69)",{"type":40,"tag":168,"props":1925,"children":1926},{},[1927,1929,1933],{"type":45,"value":1928},"\u003C 1.2 → ",{"type":40,"tag":54,"props":1930,"children":1931},{},[1932],{"type":45,"value":856},{"type":45,"value":1934}," (band 0–59)",{"type":40,"tag":168,"props":1936,"children":1937},{},[1938,1940,1945,1947,1951,1953,1957,1959,1963,1965,1969],{"type":45,"value":1939},"The overall grade is ",{"type":40,"tag":54,"props":1941,"children":1942},{},[1943],{"type":45,"value":1944},"capped at the worst sub-grade",{"type":45,"value":1946}," — if any sub-grade\nis ",{"type":40,"tag":54,"props":1948,"children":1949},{},[1950],{"type":45,"value":856},{"type":45,"value":1952},", the overall grade is ",{"type":40,"tag":54,"props":1954,"children":1955},{},[1956],{"type":45,"value":856},{"type":45,"value":1958},"; if the worst sub-grade is ",{"type":40,"tag":54,"props":1960,"children":1961},{},[1962],{"type":45,"value":817},{"type":45,"value":1964},",\nthe overall grade is at most ",{"type":40,"tag":54,"props":1966,"children":1967},{},[1968],{"type":45,"value":817},{"type":45,"value":1970},"; and so on. A test that fails on any\none dimension cannot earn a higher overall grade than that dimension.",{"type":40,"tag":48,"props":1972,"children":1973},{},[1974,1976,1981,1983,1988],{"type":45,"value":1975},"Report the ",{"type":40,"tag":54,"props":1977,"children":1978},{},[1979],{"type":45,"value":1980},"letter grade",{"type":45,"value":1982}," and the ",{"type":40,"tag":54,"props":1984,"children":1985},{},[1986],{"type":45,"value":1987},"score band",{"type":45,"value":1989}," (not a single 0–100\nnumber). False precision invites bikeshedding; bands keep the conversation\nfocused on the rubric.",{"type":40,"tag":411,"props":1991,"children":1993},{"id":1992},"step-4-build-the-note",[1994],{"type":45,"value":1995},"Step 4: Build the note",{"type":40,"tag":48,"props":1997,"children":1998},{},[1999],{"type":45,"value":2000},"The note column is one short sentence (target ≤ 120 characters). State the\nsingle most important reason for the grade. Examples:",{"type":40,"tag":164,"props":2002,"children":2003},{},[2004,2015,2026,2037,2048],{"type":40,"tag":168,"props":2005,"children":2006},{},[2007,2009],{"type":45,"value":2008},"A (90–100): ",{"type":40,"tag":76,"props":2010,"children":2012},{"className":2011},[],[2013],{"type":45,"value":2014},"Clear AAA structure; equality + exception assertions on the public contract.",{"type":40,"tag":168,"props":2016,"children":2017},{},[2018,2020],{"type":45,"value":2019},"B (80–89): ",{"type":40,"tag":76,"props":2021,"children":2023},{"className":2022},[],[2024],{"type":45,"value":2025},"Good assertion variety, mildly long body — consider splitting into per-condition tests.",{"type":40,"tag":168,"props":2027,"children":2028},{},[2029,2031],{"type":45,"value":2030},"C (70–79): ",{"type":40,"tag":76,"props":2032,"children":2034},{"className":2033},[],[2035],{"type":45,"value":2036},"Only checks IsNotNull on the result; no value verification.",{"type":40,"tag":168,"props":2038,"children":2039},{},[2040,2042],{"type":45,"value":2041},"D (60–69): ",{"type":40,"tag":76,"props":2043,"children":2045},{"className":2044},[],[2046],{"type":45,"value":2047},"Self-referential assertion: round-trip identity verifies plumbing, not transformation.",{"type":40,"tag":168,"props":2049,"children":2050},{},[2051,2053],{"type":45,"value":2052},"F (0–59): ",{"type":40,"tag":76,"props":2054,"children":2056},{"className":2055},[],[2057],{"type":45,"value":2058},"No assertions — test executes the method but never verifies anything.",{"type":40,"tag":48,"props":2060,"children":2061},{},[2062,2064,2070],{"type":45,"value":2063},"If a test gets A with no notable issues, the note may simply be\n",{"type":40,"tag":76,"props":2065,"children":2067},{"className":2066},[],[2068],{"type":45,"value":2069},"No issues found.",{"type":45,"value":2071}," — do not invent weaknesses to justify the grade.",{"type":40,"tag":411,"props":2073,"children":2075},{"id":2074},"step-5-report",[2076],{"type":45,"value":2077},"Step 5: Report",{"type":40,"tag":48,"props":2079,"children":2080},{},[2081],{"type":45,"value":2082},"Produce two sections.",{"type":40,"tag":645,"props":2084,"children":2086},{"id":2085},"_1-summary",[2087],{"type":45,"value":2088},"1. Summary",{"type":40,"tag":48,"props":2090,"children":2091},{},[2092],{"type":45,"value":2093},"A short paragraph (2–4 sentences) covering: total tests graded, grade\ndistribution, most common issue, and the single most important\nrecommendation.",{"type":40,"tag":645,"props":2095,"children":2097},{"id":2096},"_2-per-test-table",[2098],{"type":45,"value":2099},"2. Per-test table",{"type":40,"tag":2101,"props":2102,"children":2107},"pre",{"className":2103,"code":2104,"language":2105,"meta":2106,"style":2106},"language-markdown shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","| Test | Grade | Band | Notes |\n|------|-------|------|-------|\n| `Namespace.ClassName.Test_Method_Condition_Expected` | A | 90–100 | Clear AAA; equality + exception assertions. |\n| `Namespace.ClassName.Test_Other` | C | 70–79 | Only `IsNotNull` — no value verification. |\n| `Namespace.ClassName.Test_Old` | F | 0–59 | No assertions. |\n","markdown","",[2108],{"type":40,"tag":76,"props":2109,"children":2110},{"__ignoreMap":2106},[2111,2161,2170,2226,2295],{"type":40,"tag":2112,"props":2113,"children":2116},"span",{"class":2114,"line":2115},"line",1,[2117,2123,2129,2133,2138,2142,2147,2151,2156],{"type":40,"tag":2112,"props":2118,"children":2120},{"style":2119},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[2121],{"type":45,"value":2122},"|",{"type":40,"tag":2112,"props":2124,"children":2126},{"style":2125},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[2127],{"type":45,"value":2128}," Test ",{"type":40,"tag":2112,"props":2130,"children":2131},{"style":2119},[2132],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2134,"children":2135},{"style":2125},[2136],{"type":45,"value":2137}," Grade ",{"type":40,"tag":2112,"props":2139,"children":2140},{"style":2119},[2141],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2143,"children":2144},{"style":2125},[2145],{"type":45,"value":2146}," Band ",{"type":40,"tag":2112,"props":2148,"children":2149},{"style":2119},[2150],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2152,"children":2153},{"style":2125},[2154],{"type":45,"value":2155}," Notes ",{"type":40,"tag":2112,"props":2157,"children":2158},{"style":2119},[2159],{"type":45,"value":2160},"|\n",{"type":40,"tag":2112,"props":2162,"children":2164},{"class":2114,"line":2163},2,[2165],{"type":40,"tag":2112,"props":2166,"children":2167},{"style":2119},[2168],{"type":45,"value":2169},"|------|-------|------|-------|\n",{"type":40,"tag":2112,"props":2171,"children":2173},{"class":2114,"line":2172},3,[2174,2178,2183,2189,2194,2199,2204,2208,2213,2217,2222],{"type":40,"tag":2112,"props":2175,"children":2176},{"style":2119},[2177],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2179,"children":2180},{"style":2119},[2181],{"type":45,"value":2182}," `",{"type":40,"tag":2112,"props":2184,"children":2186},{"style":2185},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[2187],{"type":45,"value":2188},"Namespace.ClassName.Test_Method_Condition_Expected",{"type":40,"tag":2112,"props":2190,"children":2191},{"style":2119},[2192],{"type":45,"value":2193},"`",{"type":40,"tag":2112,"props":2195,"children":2196},{"style":2119},[2197],{"type":45,"value":2198}," |",{"type":40,"tag":2112,"props":2200,"children":2201},{"style":2125},[2202],{"type":45,"value":2203}," A ",{"type":40,"tag":2112,"props":2205,"children":2206},{"style":2119},[2207],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2209,"children":2210},{"style":2125},[2211],{"type":45,"value":2212}," 90–100 ",{"type":40,"tag":2112,"props":2214,"children":2215},{"style":2119},[2216],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2218,"children":2219},{"style":2125},[2220],{"type":45,"value":2221}," Clear AAA; equality + exception assertions. ",{"type":40,"tag":2112,"props":2223,"children":2224},{"style":2119},[2225],{"type":45,"value":2160},{"type":40,"tag":2112,"props":2227,"children":2229},{"class":2114,"line":2228},4,[2230,2234,2238,2243,2247,2251,2256,2260,2265,2269,2274,2278,2282,2286,2291],{"type":40,"tag":2112,"props":2231,"children":2232},{"style":2119},[2233],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2235,"children":2236},{"style":2119},[2237],{"type":45,"value":2182},{"type":40,"tag":2112,"props":2239,"children":2240},{"style":2185},[2241],{"type":45,"value":2242},"Namespace.ClassName.Test_Other",{"type":40,"tag":2112,"props":2244,"children":2245},{"style":2119},[2246],{"type":45,"value":2193},{"type":40,"tag":2112,"props":2248,"children":2249},{"style":2119},[2250],{"type":45,"value":2198},{"type":40,"tag":2112,"props":2252,"children":2253},{"style":2125},[2254],{"type":45,"value":2255}," C ",{"type":40,"tag":2112,"props":2257,"children":2258},{"style":2119},[2259],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2261,"children":2262},{"style":2125},[2263],{"type":45,"value":2264}," 70–79 ",{"type":40,"tag":2112,"props":2266,"children":2267},{"style":2119},[2268],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2270,"children":2271},{"style":2125},[2272],{"type":45,"value":2273}," Only ",{"type":40,"tag":2112,"props":2275,"children":2276},{"style":2119},[2277],{"type":45,"value":2193},{"type":40,"tag":2112,"props":2279,"children":2280},{"style":2185},[2281],{"type":45,"value":789},{"type":40,"tag":2112,"props":2283,"children":2284},{"style":2119},[2285],{"type":45,"value":2193},{"type":40,"tag":2112,"props":2287,"children":2288},{"style":2125},[2289],{"type":45,"value":2290}," — no value verification. ",{"type":40,"tag":2112,"props":2292,"children":2293},{"style":2119},[2294],{"type":45,"value":2160},{"type":40,"tag":2112,"props":2296,"children":2298},{"class":2114,"line":2297},5,[2299,2303,2307,2312,2316,2320,2325,2329,2334,2338,2343],{"type":40,"tag":2112,"props":2300,"children":2301},{"style":2119},[2302],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2304,"children":2305},{"style":2119},[2306],{"type":45,"value":2182},{"type":40,"tag":2112,"props":2308,"children":2309},{"style":2185},[2310],{"type":45,"value":2311},"Namespace.ClassName.Test_Old",{"type":40,"tag":2112,"props":2313,"children":2314},{"style":2119},[2315],{"type":45,"value":2193},{"type":40,"tag":2112,"props":2317,"children":2318},{"style":2119},[2319],{"type":45,"value":2198},{"type":40,"tag":2112,"props":2321,"children":2322},{"style":2125},[2323],{"type":45,"value":2324}," F ",{"type":40,"tag":2112,"props":2326,"children":2327},{"style":2119},[2328],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2330,"children":2331},{"style":2125},[2332],{"type":45,"value":2333}," 0–59 ",{"type":40,"tag":2112,"props":2335,"children":2336},{"style":2119},[2337],{"type":45,"value":2122},{"type":40,"tag":2112,"props":2339,"children":2340},{"style":2125},[2341],{"type":45,"value":2342}," No assertions. ",{"type":40,"tag":2112,"props":2344,"children":2345},{"style":2119},[2346],{"type":45,"value":2160},{"type":40,"tag":48,"props":2348,"children":2349},{},[2350,2355],{"type":40,"tag":54,"props":2351,"children":2352},{},[2353],{"type":45,"value":2354},"Caps and ordering",{"type":45,"value":2356},":",{"type":40,"tag":164,"props":2358,"children":2359},{},[2360,2386,2391],{"type":40,"tag":168,"props":2361,"children":2362},{},[2363,2365,2370,2372,2376,2378,2384],{"type":45,"value":2364},"If the table would exceed ",{"type":40,"tag":54,"props":2366,"children":2367},{},[2368],{"type":45,"value":2369},"50 rows",{"type":45,"value":2371},", show all tests graded below ",{"type":40,"tag":54,"props":2373,"children":2374},{},[2375],{"type":45,"value":762},{"type":45,"value":2377},"\nfirst (worst to best), then a sample of the best tests, and wrap any\noverflow in a collapsed ",{"type":40,"tag":76,"props":2379,"children":2381},{"className":2380},[],[2382],{"type":45,"value":2383},"\u003Cdetails>",{"type":45,"value":2385}," block.",{"type":40,"tag":168,"props":2387,"children":2388},{},[2389],{"type":45,"value":2390},"Within the same grade, order by file path then by method name for\ndeterminism.",{"type":40,"tag":168,"props":2392,"children":2393},{},[2394,2396,2402,2404,2410],{"type":45,"value":2395},"If the diff context is provided, prefix each test name with a ",{"type":40,"tag":76,"props":2397,"children":2399},{"className":2398},[],[2400],{"type":45,"value":2401},"(new)",{"type":45,"value":2403}," or\n",{"type":40,"tag":76,"props":2405,"children":2407},{"className":2406},[],[2408],{"type":45,"value":2409},"(modified)",{"type":45,"value":2411}," marker.",{"type":40,"tag":48,"props":2413,"children":2414},{},[2415],{"type":45,"value":2416},"If multiple languages are present, produce one table per language and\nprefix each section with the language name and framework.",{"type":40,"tag":116,"props":2418,"children":2420},{"id":2419},"validation",[2421],{"type":45,"value":2422},"Validation",{"type":40,"tag":164,"props":2424,"children":2427},{"className":2425},[2426],"contains-task-list",[2428,2447,2456,2472,2481,2490,2522,2531,2561,2599],{"type":40,"tag":168,"props":2429,"children":2432},{"className":2430},[2431],"task-list-item",[2433,2439,2441,2446],{"type":40,"tag":2434,"props":2435,"children":2438},"input",{"disabled":2436,"type":2437},true,"checkbox",[],{"type":45,"value":2440}," Every test in the input list appears in the table (or is recorded as\n",{"type":40,"tag":76,"props":2442,"children":2444},{"className":2443},[],[2445],{"type":45,"value":597},{"type":45,"value":845},{"type":40,"tag":168,"props":2448,"children":2450},{"className":2449},[2431],[2451,2454],{"type":40,"tag":2434,"props":2452,"children":2453},{"disabled":2436,"type":2437},[],{"type":45,"value":2455}," Every grade is justified by at least one observable signal in the\ncaptured body — no speculative deductions.",{"type":40,"tag":168,"props":2457,"children":2459},{"className":2458},[2431],[2460,2463,2465,2470],{"type":40,"tag":2434,"props":2461,"children":2462},{"disabled":2436,"type":2437},[],{"type":45,"value":2464}," Trivial-assertion tests are flagged only when the ",{"type":40,"tag":54,"props":2466,"children":2467},{},[2468],{"type":45,"value":2469},"only",{"type":45,"value":2471}," assertion\nis trivial (a null check before a meaningful assertion is not trivial).",{"type":40,"tag":168,"props":2473,"children":2475},{"className":2474},[2431],[2476,2479],{"type":40,"tag":2434,"props":2477,"children":2478},{"disabled":2436,"type":2437},[],{"type":45,"value":2480}," Exception-only tests are not penalized for low assertion count.",{"type":40,"tag":168,"props":2482,"children":2484},{"className":2483},[2431],[2485,2488],{"type":40,"tag":2434,"props":2486,"children":2487},{"disabled":2436,"type":2437},[],{"type":45,"value":2489}," Mock-call verifications and bare assertion forms count as real\nassertions of the appropriate category.",{"type":40,"tag":168,"props":2491,"children":2493},{"className":2492},[2431],[2494,2497,2499,2505,2507,2513,2514,2520],{"type":40,"tag":2434,"props":2495,"children":2496},{"disabled":2436,"type":2437},[],{"type":45,"value":2498}," Boolean assertions on meaningful properties (",{"type":40,"tag":76,"props":2500,"children":2502},{"className":2501},[],[2503],{"type":45,"value":2504},"Assert.IsTrue(result.IsValid)",{"type":45,"value":2506},")\nare not classified as always-true; only literal ",{"type":40,"tag":76,"props":2508,"children":2510},{"className":2509},[],[2511],{"type":45,"value":2512},"true",{"type":45,"value":906},{"type":40,"tag":76,"props":2515,"children":2517},{"className":2516},[],[2518],{"type":45,"value":2519},"false",{"type":45,"value":2521}," constants are.",{"type":40,"tag":168,"props":2523,"children":2525},{"className":2524},[2431],[2526,2529],{"type":40,"tag":2434,"props":2527,"children":2528},{"disabled":2436,"type":2437},[],{"type":45,"value":2530}," Self-referential assertions are flagged separately from normal\nequality assertions.",{"type":40,"tag":168,"props":2532,"children":2534},{"className":2533},[2431],[2535,2538,2540,2545,2546,2551,2553,2559],{"type":40,"tag":2434,"props":2536,"children":2537},{"disabled":2436,"type":2437},[],{"type":45,"value":2539}," Idiomatic patterns are not flagged: Go\u002FRust table-driven sub-tests,\npytest bare ",{"type":40,"tag":76,"props":2541,"children":2543},{"className":2542},[],[2544],{"type":45,"value":733},{"type":45,"value":735},{"type":40,"tag":76,"props":2547,"children":2549},{"className":2548},[],[2550],{"type":45,"value":741},{"type":45,"value":2552},",\nJS\u002FTS ",{"type":40,"tag":76,"props":2554,"children":2556},{"className":2555},[],[2557],{"type":45,"value":2558},"expect(mock).toHaveBeenCalledWith(...)",{"type":45,"value":2560},".",{"type":40,"tag":168,"props":2562,"children":2564},{"className":2563},[2431],[2565,2568,2570,2576,2577,2583,2584,2590,2592,2597],{"type":40,"tag":2434,"props":2566,"children":2567},{"disabled":2436,"type":2437},[],{"type":45,"value":2569}," Async test pitfalls (un-awaited ",{"type":40,"tag":76,"props":2571,"children":2573},{"className":2572},[],[2574],{"type":45,"value":2575},"resolves",{"type":45,"value":906},{"type":40,"tag":76,"props":2578,"children":2580},{"className":2579},[],[2581],{"type":45,"value":2582},"rejects",{"type":45,"value":906},{"type":40,"tag":76,"props":2585,"children":2587},{"className":2586},[],[2588],{"type":45,"value":2589},"ThrowsAsync",{"type":45,"value":2591},",\npytest-asyncio without ",{"type":40,"tag":76,"props":2593,"children":2595},{"className":2594},[],[2596],{"type":45,"value":904},{"type":45,"value":2598},") drop the Assertion sub-grade to F.",{"type":40,"tag":168,"props":2600,"children":2602},{"className":2601},[2431],[2603,2606],{"type":40,"tag":2434,"props":2604,"children":2605},{"disabled":2436,"type":2437},[],{"type":45,"value":2607}," The summary leads with the highest-leverage observation, not a recap\nof the table.",{"type":40,"tag":116,"props":2609,"children":2611},{"id":2610},"common-pitfalls",[2612],{"type":45,"value":2613},"Common Pitfalls",{"type":40,"tag":292,"props":2615,"children":2616},{},[2617,2633],{"type":40,"tag":296,"props":2618,"children":2619},{},[2620],{"type":40,"tag":300,"props":2621,"children":2622},{},[2623,2628],{"type":40,"tag":304,"props":2624,"children":2625},{},[2626],{"type":45,"value":2627},"Pitfall",{"type":40,"tag":304,"props":2629,"children":2630},{},[2631],{"type":45,"value":2632},"Solution",{"type":40,"tag":320,"props":2634,"children":2635},{},[2636,2649,2662,2675,2694,2720,2753,2766,2794,2815,2828,2847,2860],{"type":40,"tag":300,"props":2637,"children":2638},{},[2639,2644],{"type":40,"tag":327,"props":2640,"children":2641},{},[2642],{"type":45,"value":2643},"Grading every test in the workspace when no list is provided",{"type":40,"tag":327,"props":2645,"children":2646},{},[2647],{"type":45,"value":2648},"Ask the caller for the explicit list; this skill is for curated input.",{"type":40,"tag":300,"props":2650,"children":2651},{},[2652,2657],{"type":40,"tag":327,"props":2653,"children":2654},{},[2655],{"type":45,"value":2656},"Inflating deductions to justify the grade",{"type":40,"tag":327,"props":2658,"children":2659},{},[2660],{"type":45,"value":2661},"Start at A; deduct only for observable issues.",{"type":40,"tag":300,"props":2663,"children":2664},{},[2665,2670],{"type":40,"tag":327,"props":2666,"children":2667},{},[2668],{"type":45,"value":2669},"Penalizing exception tests for low assertion count",{"type":40,"tag":327,"props":2671,"children":2672},{},[2673],{"type":45,"value":2674},"Exception assertions are complete on their own.",{"type":40,"tag":300,"props":2676,"children":2677},{},[2678,2689],{"type":40,"tag":327,"props":2679,"children":2680},{},[2681,2683],{"type":45,"value":2682},"Downgrading a focused Go error-path test because it checks only ",{"type":40,"tag":76,"props":2684,"children":2686},{"className":2685},[],[2687],{"type":45,"value":2688},"err != nil",{"type":40,"tag":327,"props":2690,"children":2691},{},[2692],{"type":45,"value":2693},"Expected-error existence is the observable contract for that scope; keep it at A unless the production contract requires a specific error identity or message.",{"type":40,"tag":300,"props":2695,"children":2696},{},[2697,2709],{"type":40,"tag":327,"props":2698,"children":2699},{},[2700,2702,2707],{"type":45,"value":2701},"Treating ",{"type":40,"tag":76,"props":2703,"children":2705},{"className":2704},[],[2706],{"type":45,"value":789},{"type":45,"value":2708}," before a value assertion as trivial",{"type":40,"tag":327,"props":2710,"children":2711},{},[2712,2714,2718],{"type":45,"value":2713},"Only flag when the null check is the ",{"type":40,"tag":54,"props":2715,"children":2716},{},[2717],{"type":45,"value":2469},{"type":45,"value":2719}," assertion.",{"type":40,"tag":300,"props":2721,"children":2722},{},[2723,2728],{"type":40,"tag":327,"props":2724,"children":2725},{},[2726],{"type":45,"value":2727},"Treating any Boolean assertion as effectively assertion-free",{"type":40,"tag":327,"props":2729,"children":2730},{},[2731,2733,2738,2739,2744,2746,2751],{"type":45,"value":2732},"Only always-true literals (",{"type":40,"tag":76,"props":2734,"children":2736},{"className":2735},[],[2737],{"type":45,"value":874},{"type":45,"value":99},{"type":40,"tag":76,"props":2740,"children":2742},{"className":2741},[],[2743],{"type":45,"value":881},{"type":45,"value":2745},") are; meaningful ",{"type":40,"tag":76,"props":2747,"children":2749},{"className":2748},[],[2750],{"type":45,"value":2504},{"type":45,"value":2752}," is a real assertion.",{"type":40,"tag":300,"props":2754,"children":2755},{},[2756,2761],{"type":40,"tag":327,"props":2757,"children":2758},{},[2759],{"type":45,"value":2760},"Flagging Go\u002FRust table-driven loops as conditional logic",{"type":40,"tag":327,"props":2762,"children":2763},{},[2764],{"type":45,"value":2765},"They are idiomatic; do not deduct.",{"type":40,"tag":300,"props":2767,"children":2768},{},[2769,2789],{"type":40,"tag":327,"props":2770,"children":2771},{},[2772,2774,2779,2781,2787],{"type":45,"value":2773},"Treating pytest bare ",{"type":40,"tag":76,"props":2775,"children":2777},{"className":2776},[],[2778],{"type":45,"value":733},{"type":45,"value":2780}," or Go ",{"type":40,"tag":76,"props":2782,"children":2784},{"className":2783},[],[2785],{"type":45,"value":2786},"if got != want { t.Error… }",{"type":45,"value":2788}," as missing-framework",{"type":40,"tag":327,"props":2790,"children":2791},{},[2792],{"type":45,"value":2793},"Both are canonical; count in the correct assertion category.",{"type":40,"tag":300,"props":2795,"children":2796},{},[2797,2802],{"type":40,"tag":327,"props":2798,"children":2799},{},[2800],{"type":45,"value":2801},"Penalizing tests when production code is unavailable",{"type":40,"tag":327,"props":2803,"children":2804},{},[2805,2807,2813],{"type":45,"value":2806},"Mark concerns about uncovered behaviors as ",{"type":40,"tag":76,"props":2808,"children":2810},{"className":2809},[],[2811],{"type":45,"value":2812},"Unverified",{"type":45,"value":2814}," and do not deduct.",{"type":40,"tag":300,"props":2816,"children":2817},{},[2818,2823],{"type":40,"tag":327,"props":2819,"children":2820},{},[2821],{"type":45,"value":2822},"Using a fake-precise score (e.g., 87\u002F100)",{"type":40,"tag":327,"props":2824,"children":2825},{},[2826],{"type":45,"value":2827},"Use the score band only — 90–100, 80–89, 70–79, 60–69, 0–59.",{"type":40,"tag":300,"props":2829,"children":2830},{},[2831,2836],{"type":40,"tag":327,"props":2832,"children":2833},{},[2834],{"type":45,"value":2835},"Spilling a 500-row table into a PR comment",{"type":40,"tag":327,"props":2837,"children":2838},{},[2839,2841,2846],{"type":45,"value":2840},"Apply the row cap from Step 5; collapse extras into ",{"type":40,"tag":76,"props":2842,"children":2844},{"className":2843},[],[2845],{"type":45,"value":2383},{"type":45,"value":2560},{"type":40,"tag":300,"props":2848,"children":2849},{},[2850,2855],{"type":40,"tag":327,"props":2851,"children":2852},{},[2853],{"type":45,"value":2854},"Re-reporting an existing finding three times under different categories",{"type":40,"tag":327,"props":2856,"children":2857},{},[2858],{"type":45,"value":2859},"Pick the most fitting category and report once.",{"type":40,"tag":300,"props":2861,"children":2862},{},[2863,2868],{"type":40,"tag":327,"props":2864,"children":2865},{},[2866],{"type":45,"value":2867},"Inventing weaknesses for A-grade tests to make the note \"balanced\"",{"type":40,"tag":327,"props":2869,"children":2870},{},[2871,2873],{"type":45,"value":2872},"If a test is clean, the note may simply read ",{"type":40,"tag":76,"props":2874,"children":2876},{"className":2875},[],[2877],{"type":45,"value":2069},{"type":40,"tag":2879,"props":2880,"children":2881},"style",{},[2882],{"type":45,"value":2883},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2885,"total":3045},[2886,2902,2917,2932,2950,2959,2979,2989,3001,3011,3024,3035],{"slug":2887,"name":2887,"fn":2888,"description":2889,"org":2890,"tags":2891,"stars":2899,"repoUrl":2900,"updatedAt":2901},"multithreaded-task-migration","migrate MSBuild tasks to multithreaded mode","Guide for migrating MSBuild tasks to multithreaded mode support, including compatibility red-team review. Use this when converting tasks to thread-safe versions, implementing IMultiThreadableTask, adding TaskEnvironment support, or auditing migrations for behavioral compatibility.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2892,2895,2896],{"name":2893,"slug":2894,"type":15},".NET","net",{"name":17,"slug":18,"type":15},{"name":2897,"slug":2898,"type":15},"Performance","performance",5535,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fmsbuild","2026-07-22T05:37:33.965588",{"slug":2903,"name":2903,"fn":2904,"description":2905,"org":2906,"tags":2907,"stars":22,"repoUrl":23,"updatedAt":2916},"analyzing-dotnet-performance","analyze .NET code for performance anti-patterns","Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I\u002FO with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2908,2909,2912,2915],{"name":2893,"slug":2894,"type":15},{"name":2910,"slug":2911,"type":15},"Code Analysis","code-analysis",{"name":2913,"slug":2914,"type":15},"Debugging","debugging",{"name":2897,"slug":2898,"type":15},"2026-07-12T08:23:25.400375",{"slug":2918,"name":2918,"fn":2919,"description":2920,"org":2921,"tags":2922,"stars":22,"repoUrl":23,"updatedAt":2931},"android-tombstone-symbolication","symbolicate .NET runtime frames in Android tombstones","Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java\u002FKotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2923,2924,2927,2928],{"name":2893,"slug":2894,"type":15},{"name":2925,"slug":2926,"type":15},"Android","android",{"name":2913,"slug":2914,"type":15},{"name":2929,"slug":2930,"type":15},"Microsoft","microsoft","2026-07-12T08:23:21.595572",{"slug":2933,"name":2933,"fn":2934,"description":2935,"org":2936,"tags":2937,"stars":22,"repoUrl":23,"updatedAt":2949},"apple-crash-symbolication","symbolicate .NET runtime frames in crash logs","Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift\u002FObjective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2938,2939,2940,2943,2946],{"name":2893,"slug":2894,"type":15},{"name":2913,"slug":2914,"type":15},{"name":2941,"slug":2942,"type":15},"iOS","ios",{"name":2944,"slug":2945,"type":15},"macOS","macos",{"name":2947,"slug":2948,"type":15},"Observability","observability","2026-07-12T08:23:20.369986",{"slug":139,"name":139,"fn":2951,"description":2952,"org":2953,"tags":2954,"stars":22,"repoUrl":23,"updatedAt":2958},"evaluate assertion quality in test suites","Analyzes the variety and depth of assertions across test suites in any language. Use when the user asks to evaluate assertion quality, find shallow tests, identify assertion-free tests (no assertions or only trivial ones like Assert.IsNotNull \u002F toBeTruthy()), flag self-referential or tautological assertions, measure assertion diversity, or audit whether tests verify different facets of behavior. Polyglot: .NET, Python, TS\u002FJS, Java, Go, Ruby, Rust, Swift, Kotlin, PowerShell, C++. DO NOT USE FOR: writing new tests (use code-testing-agent \u002F writing-mstest-tests), mutation reasoning about whether tests would catch a bug (use test-gap-analysis), or a general severity-ranked anti-pattern audit (use test-anti-patterns), fixing or rewriting assertions, or writing, fixing, or modernizing MSTest tests, assertions, or attributes (use writing-mstest-tests).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2955,2956,2957],{"name":2910,"slug":2911,"type":15},{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},"2026-07-12T08:23:51.277743",{"slug":2960,"name":2960,"fn":2961,"description":2962,"org":2963,"tags":2964,"stars":22,"repoUrl":23,"updatedAt":2978},"author-component","create and review Blazor components","Create or review Blazor components (.razor files) with correct architecture. USE FOR: writing new Blazor components that do NOT involve JavaScript interop, implementing parameters and EventCallback, RenderFragment slots, component lifecycle (OnInitializedAsync, OnParametersSet), async patterns, IAsyncDisposable, CancellationToken, CSS isolation, code-behind. DO NOT USE FOR: creating new projects (use create-blazor-project), JavaScript interop or calling browser APIs from Blazor (use use-js-interop), forms and validation (use collect-user-input), prerendering issues (use support-prerendering), HTTP data fetching patterns (use fetch-and-send-data), coordinating state between unrelated components (use coordinate-components).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2965,2966,2969,2972,2975],{"name":2893,"slug":2894,"type":15},{"name":2967,"slug":2968,"type":15},"Blazor","blazor",{"name":2970,"slug":2971,"type":15},"C#","csharp",{"name":2973,"slug":2974,"type":15},"UI Components","ui-components",{"name":2976,"slug":2977,"type":15},"Web Development","web-development","2026-07-15T06:03:29.216359",{"slug":2980,"name":2980,"fn":2981,"description":2982,"org":2983,"tags":2984,"stars":22,"repoUrl":23,"updatedAt":2988},"binlog-failure-analysis","analyze MSBuild binary logs","Analyze MSBuild binary logs to diagnose build failures. USE FOR: build errors that are unclear from console output, diagnosing cascading failures across multi-project builds, tracing MSBuild target execution order, and generally any MSBuild build issues. Requires an existing .binlog file. DO NOT USE FOR: generating binlogs (use binlog-generation), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2985,2986,2987],{"name":2910,"slug":2911,"type":15},{"name":2913,"slug":2914,"type":15},{"name":2929,"slug":2930,"type":15},"2026-07-12T08:21:34.637923",{"slug":2990,"name":2990,"fn":2991,"description":2992,"org":2993,"tags":2994,"stars":22,"repoUrl":23,"updatedAt":3000},"binlog-generation","generate MSBuild binary logs for diagnostics","Generate MSBuild binary logs (binlogs) for build diagnostics and analysis. USE FOR: adding \u002Fbl:{} to any dotnet build, test, pack, publish, or restore command to capture a full build execution trace, prerequisite for binlog-failure-analysis and build-perf-diagnostics skills, enabling post-build investigation of errors or performance. Requires MSBuild 17.8+ \u002F .NET 8 SDK+ for {} placeholder; PowerShell needs -bl:{{}}. DO NOT USE FOR: non-MSBuild build systems (npm, Maven, CMake), analyzing an existing binlog (use binlog-failure-analysis instead).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2995,2998,2999],{"name":2996,"slug":2997,"type":15},"Build","build",{"name":2913,"slug":2914,"type":15},{"name":17,"slug":18,"type":15},"2026-07-19T05:38:19.340791",{"slug":3002,"name":3002,"fn":3003,"description":3004,"org":3005,"tags":3006,"stars":22,"repoUrl":23,"updatedAt":3010},"build-parallelism","optimize MSBuild build parallelism","Diagnose and fix under-parallelized MSBuild builds. USE WHEN a multi-project solution build is slower than expected, doesn't speed up when you add cores, pegs a single core while others idle, or you want to know why `-m` isn't helping. Note: `\u002Fmaxcpucount` default is 1 (sequential) — always pass `-m` for parallel builds. Covers finding the critical path (longest serial ProjectReference chain), graph build (`\u002Fgraph`), BuildInParallel, and solution filters (`.slnf`). DO NOT USE FOR: single-project builds, incremental issues (use incremental-build), compilation slowness inside one project (use build-perf-diagnostics), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3007,3008,3009],{"name":2893,"slug":2894,"type":15},{"name":17,"slug":18,"type":15},{"name":2897,"slug":2898,"type":15},"2026-07-19T05:38:18.364937",{"slug":3012,"name":3012,"fn":3013,"description":3014,"org":3015,"tags":3016,"stars":22,"repoUrl":23,"updatedAt":3023},"build-perf-baseline","establish and optimize build performance baselines","Establish build performance baselines and apply systematic optimization techniques. USE FOR: diagnosing slow builds, establishing before\u002Fafter measurements (cold, warm, no-op scenarios), applying optimization strategies like MSBuild Server, static graph builds, artifacts output, and dependency graph trimming. Start here before diving into build-perf-diagnostics, incremental-build, or build-parallelism. DO NOT USE FOR: non-MSBuild build systems, detailed bottleneck analysis (use build-perf-diagnostics after baselining).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3017,3018,3021,3022],{"name":17,"slug":18,"type":15},{"name":3019,"slug":3020,"type":15},"Monitoring","monitoring",{"name":2897,"slug":2898,"type":15},{"name":20,"slug":21,"type":15},"2026-07-12T08:21:35.865649",{"slug":3025,"name":3025,"fn":3026,"description":3027,"org":3028,"tags":3029,"stars":22,"repoUrl":23,"updatedAt":3034},"build-perf-diagnostics","diagnose MSBuild build performance bottlenecks","Diagnose MSBuild build performance bottlenecks using binary log analysis. USE FOR: identifying why builds are slow by analyzing binlog performance summaries, detecting ResolveAssemblyReference (RAR) taking >5s, Roslyn analyzers consuming >30% of Csc time, single targets dominating >50% of build time, node utilization below 80%, excessive Copy tasks, NuGet restore running every build. Covers timeline analysis, Target\u002FTask Performance Summary interpretation, and 7 common bottleneck categories. Use after build-perf-baseline has established measurements. DO NOT USE FOR: establishing initial baselines (use build-perf-baseline first), fixing incremental build issues (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3030,3031,3032,3033],{"name":2893,"slug":2894,"type":15},{"name":2913,"slug":2914,"type":15},{"name":17,"slug":18,"type":15},{"name":2897,"slug":2898,"type":15},"2026-07-12T08:21:40.961722",{"slug":3036,"name":3036,"fn":3037,"description":3038,"org":3039,"tags":3040,"stars":22,"repoUrl":23,"updatedAt":3044},"check-bin-obj-clash","detect MSBuild output path conflicts","Detects MSBuild projects with conflicting OutputPath or IntermediateOutputPath. USE FOR: builds failing with 'Cannot create a file when that file already exists', 'The process cannot access the file because it is being used by another process', intermittent build failures that succeed on retry, or missing\u002Foverwritten outputs in multi-project or multi-targeting builds where bin\u002Fobj (or project.assets.json) collide. Common causes: shared OutputPath, missing AppendTargetFrameworkToOutputPath, extra global properties (e.g. PublishReadyToRun), or SetTargetFramework on a ProjectReference to a single-targeting project. DO NOT USE FOR: file access errors unrelated to MSBuild (OS-level locking), single-project single-TFM builds, non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3041,3042,3043],{"name":2913,"slug":2914,"type":15},{"name":17,"slug":18,"type":15},{"name":13,"slug":14,"type":15},"2026-07-19T05:38:14.336279",144,{"items":3047,"total":3096},[3048,3055,3062,3070,3076,3084,3090],{"slug":2903,"name":2903,"fn":2904,"description":2905,"org":3049,"tags":3050,"stars":22,"repoUrl":23,"updatedAt":2916},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3051,3052,3053,3054],{"name":2893,"slug":2894,"type":15},{"name":2910,"slug":2911,"type":15},{"name":2913,"slug":2914,"type":15},{"name":2897,"slug":2898,"type":15},{"slug":2918,"name":2918,"fn":2919,"description":2920,"org":3056,"tags":3057,"stars":22,"repoUrl":23,"updatedAt":2931},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3058,3059,3060,3061],{"name":2893,"slug":2894,"type":15},{"name":2925,"slug":2926,"type":15},{"name":2913,"slug":2914,"type":15},{"name":2929,"slug":2930,"type":15},{"slug":2933,"name":2933,"fn":2934,"description":2935,"org":3063,"tags":3064,"stars":22,"repoUrl":23,"updatedAt":2949},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3065,3066,3067,3068,3069],{"name":2893,"slug":2894,"type":15},{"name":2913,"slug":2914,"type":15},{"name":2941,"slug":2942,"type":15},{"name":2944,"slug":2945,"type":15},{"name":2947,"slug":2948,"type":15},{"slug":139,"name":139,"fn":2951,"description":2952,"org":3071,"tags":3072,"stars":22,"repoUrl":23,"updatedAt":2958},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3073,3074,3075],{"name":2910,"slug":2911,"type":15},{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"slug":2960,"name":2960,"fn":2961,"description":2962,"org":3077,"tags":3078,"stars":22,"repoUrl":23,"updatedAt":2978},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3079,3080,3081,3082,3083],{"name":2893,"slug":2894,"type":15},{"name":2967,"slug":2968,"type":15},{"name":2970,"slug":2971,"type":15},{"name":2973,"slug":2974,"type":15},{"name":2976,"slug":2977,"type":15},{"slug":2980,"name":2980,"fn":2981,"description":2982,"org":3085,"tags":3086,"stars":22,"repoUrl":23,"updatedAt":2988},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3087,3088,3089],{"name":2910,"slug":2911,"type":15},{"name":2913,"slug":2914,"type":15},{"name":2929,"slug":2930,"type":15},{"slug":2990,"name":2990,"fn":2991,"description":2992,"org":3091,"tags":3092,"stars":22,"repoUrl":23,"updatedAt":3000},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3093,3094,3095],{"name":2996,"slug":2997,"type":15},{"name":2913,"slug":2914,"type":15},{"name":17,"slug":18,"type":15},96]