agentleFS
MicrosoftKnown publishermicrosoft/testfx/.github/skills/behavior-preserving-refactor/SKILL.md

behavior-preserving-refactor

Skill1.1k starsChanged 23 days agoMITView on GitHub

Preserve observable behavior when splitting files, moving members, consolidating helpers, or simplifying TestFx code. Use for structural refactors and file-diet implementation; inventory baseline semantics and consumers, then run focused equivalence checks.

We split this skill into the rules it gives an agent

15 rules. About 0 are common, 11 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈11

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use this recipe for the specific code being refactored, not a repository-wide audit.

    line 8
  2. Distinctive:

    Do not claim equivalence, tested behavior, or complete consumer coverage from that sample.

    line 17
  3. Distinctive:

    Do not add support for impossible states, but do not assume compiler or user input is always valid: analyzer inputs can be incomplete or …

    line 36
  4. Distinctive:

    Use existing behavior as the oracle, not the intended behavior of a new helper.

    line 39
  5. Distinctive:

    Check initialization order if moving fields between partial files.

    line 44
  6. Very specific:

    Keep target frameworks, conditional compilation, supported platforms, and package layout intact.

    line 49
  7. Very specific:

    Check applicable PublicAPI and InternalAPI shipped/unshipped baselines.

    line 52
  8. Distinctive:

    Prefer unchanged signatures and accessibility.

    line 53
  9. Distinctive:

    Before consolidating helpers, compare every caller's baseline.

    line 63
  10. Distinctive:

    Do not replace a loop with a dictionary, eager projection, sort, or single-match operator without proving duplicate handling, enumeration, …

    line 69
  11. Distinctive:

    If the size target requires behavior changes or unsafe fragmentation, stop that part of the refactor and report the remaining line count …

    line 75
  12. Distinctive:

    Do not alter semantics just to meet a file-size goal.

    line 77
  13. Distinctive:

    Before editing, run the existing focused checks when feasible and add any necessary distinguishing cases against the original …

    line 99
  14. Very specific:

    Use the repo-local toolchain and documented filtered build/test commands from .github/copilot-instructions.md.

    line 104
  15. Distinctive:

    If shipping package/source inclusion changes, validate the packed consumer layout using existing acceptance infrastructure.

    line 109

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Behavior-Preserving Refactors

Use this recipe for the specific code being refactored, not a repository-wide audit. Read .github/copilot-instructions.md and applicable directory rules. Preserve existing behavior, including surprising reachable cases; a bug fix or modernization needs separate authorization and validation.

An issue-generation workflow's bounded inspection is not implementation preflight. For example, Daily File Diet inspects only the first 100 lines and up to 50 declaration lines of one file. Keep that budget when proposing work, label unverified assumptions, and include this recipe for the later implementer. Do not claim equivalence, tested behavior, or complete consumer coverage from that sample.

1. Establish the baseline before editing

Identify the exact methods or declarations to move or extract, their callers, existing focused tests, and owning projects. Read the complete affected bodies and relevant context, not just signatures or similar-looking helpers. For each existing implementation that might share a helper, record:

BoundaryWhat must remain unchanged
SelectionIteration order, first versus last match, first matching name versus first matching typed value, exact type tests, casts, defaults, and missing-value handling.
EvaluationShort-circuit operand order, which callbacks run and how often, enumeration count, laziness, mutation, and side effects before return or failure.
Results and failuresReturn values, diagnostics and locations, output ordering, exception type and timing, cancellation, and cleanup/disposal order where affected.
CompilationNamespaces, accessibility, attributes, overload binding, target-framework guards, and native interop signatures and marshalling.
CostAllocations, materialization, boxing, repeated scans, and algorithmic complexity on affected hot paths.

Inventory reachable empty, duplicate, malformed, null, and wrong-type inputs. Do not add support for impossible states, but do not assume compiler or user input is always valid: analyzer inputs can be incomplete or contain errors. Name the existing guard that makes a case unreachable if excluding it. Use existing behavior as the oracle, not the intended behavior of a new helper.

2. Trace the actual consumers and constraints

  • Find all partial declarations and call sites of the affected type or member. Check initialization order if moving fields between partial files.
  • Inspect owning project files and imports for linked Compile items, shared source, globs, explicit includes, and packaging rules. Find every consuming project, including source-only packages; a build of the original owner does not prove that a new file is included by another consumer.
  • Keep target frameworks, conditional compilation, supported platforms, and package layout intact. A native interop file move is not permission to replace DllImport, change marshalling, or adopt APIs unavailable on older targets.
  • Check applicable PublicAPI and InternalAPI shipped/unshipped baselines. Prefer unchanged signatures and accessibility; do not expose a helper just to share it. Record required newly tracked declarations without rewriting shipped API history.

3. Make the structural change, not adjacent cleanup

Move existing bodies unchanged first, preserving attributes, guards, resource access, and file encoding. Keep unrelated renaming, formatting, LINQ rewrites, interop modernization, new validation, and behavior fixes out of the change.

Before consolidating helpers, compare every caller's baseline. Similar names or happy-path outputs do not establish equivalence. If callers differ in selection, evaluation, or failure behavior, keep separate implementations or use the smallest explicit helper that preserves each contract; do not invent a configurable abstraction merely to eliminate duplication.

Do not replace a loop with a dictionary, eager projection, sort, or single-match operator without proving duplicate handling, enumeration, and failure behavior. On hot paths, preserve allocation and complexity characteristics. Use existing allocation/performance checks when the extraction could change them; do not introduce a benchmark project for a pure move.

If the size target requires behavior changes or unsafe fragmentation, stop that part of the refactor and report the remaining line count and concrete constraint. Do not alter semantics just to meet a file-size goal.

4. Use distinguishing equivalence cases

These are examples to adapt to the actual baseline, not new product semantics:

BaselineFocused caseRequired observation
A loop overwrites its result for every matching name.Matching values 1, then 2.Result remains 2; returning the first match is not equivalent.
A loop returns the first matching name whose value is an int.Same-name string "bad", integer 7, then integer 9.Result remains 7; taking the first name then casting, or taking the last integer, is not equivalent.
Exact int type matching.Same-name long value 7L, followed by int value 8.Result remains 8; numeric conversion changes type selection.
ShouldRun() && Execute().ShouldRun returns false; Execute records a call or throws.Only ShouldRun runs; reordering operands or evaluating both eagerly is not equivalent.
A guarded interop declaration moves to another file.Build every source-linked consumer and affected supported TFM/platform path.Declaration, guards, accessibility, and marshalling remain unchanged; no new runtime API requirement.

Also cover the actual missing/empty and malformed-only outcomes, not just malformed input followed by a valid value. Assert observable values, diagnostic locations, exception behavior, and ordered side-effect traces where applicable. Pin expectations to the baseline; comparing two paths that both call the new helper is not an equivalence test.

5. Validate the smallest relevant surface

Before editing, run the existing focused checks when feasible and add any necessary distinguishing cases against the original implementation. After the change, rerun the same checks. Explain pre-existing failures or unavailable checks; do not report unrun checks as passed.

Use the repo-local toolchain and documented filtered build/test commands from .github/copilot-instructions.md. Build the owning and linked consuming projects for affected target frameworks. Run directly related tests, escalating only when dependencies or failures justify it. A pure file move needs inclusion and compilation checks plus relevant existing tests, not the full solution. If shipping package/source inclusion changes, validate the packed consumer layout using existing acceptance infrastructure.

Review the final diff for changed expressions, ordering, signatures, attributes, guards, includes, tracked APIs, and unintended adjacent edits. Measure resulting file sizes rather than assuming the split meets its target.

Completion evidence

Report the moved/extracted responsibility, baseline invariants and distinguishing cases, affected consumers/TFMs and API surfaces, exact checks and outcomes, and resulting line counts when size is the goal. Identify any remaining uncertainty or blocked target explicitly. Compilation alone does not prove runtime equivalence, and passing happy-path tests does not settle first/last selection or short-circuit behavior.

Download now (ZIP)

Trending

Quiet since September.about the whole repository

Dec 2025: 1 mentionJan 2026: 0 mentionsFeb 2026: 0 mentionsMar 2026: 14 mentionsApr 2026: 0 mentionsMay 2026: 3 mentionsJun 2026: 0 mentionsJul 2026: 0 mentionsAug 2026: 0 mentionsSep 2026: 2 mentionsOct 2026: 0 mentionsNov 2026: 0 mentions
Dec 2025Jun 2026Nov 2026
Mentions a month of the microsoft/testfx repository that our weekly crawl found. There is no adoption data for this file yet.

Who's talking about microsoft/testfx

The biggest 5 of 68 mentions linking to the repository that our crawl found. Each line is their own headline.

See all 68

Talked about on GitHub and Stack Overflow (68 times).

On GitHub · 41

36 more on GitHub

On Stack Overflow · 27

22 more on Stack Overflow

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.