characterization-test-writer
Writes pinning and characterization tests that lock in the current behavior of untested legacy code - bugs included - before a refactor, so any later behavior change trips an alarm. Use when someone says "I need a safety net before refactoring this", "this module has no tests and I have to change it", "capture what this function does today", or the correct behavior is simply whatever the code does now. Do NOT use for writing tests for new behavior built red-green - use tdd-expert instead; do NOT use to decide which code is worth testing or to rank coverage gaps - use coverage-gap-finder instead; do NOT use to verify existing assertions are strong - use mutation-test-runner instead.
- License 许可
- MIT
- 最近更新
- 2026-10-14
- Stars
- ★ 25
- Forks
- 3
01 前言 Front Matter
- name
- characterization-test-writer
- metadata
- { "title": "Characterization Test Writer" }
- description
- Writes pinning and characterization tests that lock in the current behavior of untested legacy code - bugs included - before a refactor, so any later behavior change trips an alarm. Use when someone says "I need a safety net before refactoring this", "this module has no tests and I have to change it", "capture what this function does today", or the correct behavior is simply whatever the code does now. Do NOT use for writing tests for new behavior built red-green - use tdd-expert instead; do NOT use to decide which code is worth testing or to rank coverage gaps - use coverage-gap-finder instead; do NOT use to verify existing assertions are strong - use mutation-test-runner instead.
03 SKILL.md 正文
Characterization Test Writer
Pin what legacy code currently does - bugs included - so a refactor that changes behavior fails loudly instead of silently. The costly mistake this prevents is the "clean" refactor that quietly changes an output nobody noticed was load-bearing, discovered weeks later in production. A characterization test records reality; it never judges it.
Operating procedure
Order matters: the seam must exist before you can capture output, output must be captured before you can stabilize it, and coverage is only measurable once tests run.
Step 1: Gather inputs
- The exact function/module about to change, and the refactor's blast radius - which code paths will the change touch? Only those need pinning.
- The dependency list: database, clock, RNG, network, filesystem, globals, environment. Each is a nondeterminism source to control in Step 4.
- Any recorded production inputs or logs available for replay. Label assumptions about "typical inputs" as guesses.
Step 2: Find the smallest seam
Untested legacy code reaches for databases, clocks, network, and globals, so it usually cannot be called in isolation. Introduce the minimum seam that lets you invoke it: extract a method, parameterize a constructor, inject the dependency, or subclass-and-override (the "subclass to sense" technique from Working Effectively with Legacy Code). Do not refactor the body before the net exists - seam-introduction edits must be mechanical and behavior-preserving (rename, extract, inject), nothing that changes logic.
Step 3: Capture actual output, not expected
Write a deliberately failing assertion, run it, read the real value from the failure message, and paste it in:
def test_characterize_shipping_for_oversize_order():
result = calc_shipping(weight_kg=32, region="EU")
assert result == "PLACEHOLDER" # run -> failure says: AssertionError: 47.6 != PLACEHOLDER
becomes, after one run:
def test_characterizes_oversize_eu_shipping():
result = calc_shipping(weight_kg=32, region="EU")
assert result == 47.6 # observed 47.6, not the 45.0 the spec claims - see pins_known_bug below
If the captured value looks wrong, pin it anyway and tag it (pins_known_bug_ prefix or a comment) so the team decides its fate later. Fixing it now defeats the purpose: the refactor must be provably behavior-neutral first, and the bug fix becomes its own reviewable change afterward.
Step 4: Control nondeterminism until the suite passes twice in a row
- Freeze time (freezegun, libfaketime, an injected Clock), seed RNGs, stub network and I/O, and snapshot database state.
- When output genuinely cannot be made value-deterministic (large reports, generated documents), use golden-master / approval testing (ApprovalTests, snapshot files): record the full output blob once, diff on every run, and scrub volatile fields (timestamps, IDs) in the comparison.
- The acceptance test for this step is literal: run the suite twice consecutively with zero flakes.
Step 5: Cover every branch the refactor will touch
Run branch coverage and add cases until every branch in the refactor's blast radius is exercised - branch coverage, not line coverage, because an if/else can be 100% line-covered with one arm never taken. Use parameterized tests and replay of recorded production inputs to reach edge cases you would never invent (empty input, nulls, boundary values, error paths). Code outside the blast radius does not need pinning - this is a net for one refactor, not a full suite.
Step 6: Make failures legible
Name each test for the behavior it pins (characterizes_returns_zero_for_empty_cart, not test_method_3) and keep each assertion narrow, so one regression breaks one narrowly-scoped test and the failure message names the changed behavior.
Deliverable
Produce a characterization suite plus a one-paragraph coverage note: which paths in the blast radius are pinned, the twice-in-a-row green run confirmed, and a tagged list of pinned-but-suspect outputs (pins_known_bug_*) handed to the team for post-refactor triage.
Do NOT
- Do NOT assert what the code should do - that is a normal unit test; characterization asserts what it does.
- Do NOT fix bugs discovered while pinning; tag them, pin them, fix them after the net exists.
- Do NOT perform a large refactor to make code testable before the net exists; introduce only the minimum mechanical seam.
- Do NOT characterize code about to be deleted, or code already covered by intention-revealing unit tests.
- Do NOT hand-write expected values from reading the code - every asserted value must come from a real run; reasoning to a value smuggles in your model of the code, which is exactly what is untrusted.
- Do NOT keep these tests forever; they are scaffolding. Replace them with intention-revealing tests once the refactor lands and the behavior is understood.
Quality bar
- Every asserted value was observed from a real run, never reasoned to.
- Every branch in the refactor's blast radius is covered before a line of the refactor is written.
- The suite passes twice in a row with no flakiness - nondeterminism is fully pinned.
- One regression breaks one narrowly-named test whose name states the broken behavior.
- Every pinned-but-suspect output carries a
pins_known_bug_*tag so a bug is never silently promoted to a requirement.
Escalation
Deciding which untested code deserves this treatment is coverage-gap-finder's job. Proving the finished suite's assertions actually bite is mutation-test-runner's. If the refactor is a service extraction rather than an in-process change, sequence it with monolith-decomposer, and capture the business rules inside the code with business-rule-extractor.
04 同仓库更多 Skills
有氧运动计划
zone-2-cardio-plan
当有人要求制定稳态有氧训练计划、进行有氧基础训练或达到第二区间、安排轻松或耐力型的每周有氧训练、按心率区间训练,或提高静息心率、最大摄氧量或耐力时,可以使用此方案。该方案构建了一个包含训练计划结构、训练方式选择和进度安排的心率区间有氧训练模块。本方案仅提供一般健身指导,不构成医疗建议。请勿用于设计力量或举重训练计划(请使用力量训练计划)、高强度间歇训练 (HIIT) 或循环训练(请使用体能训练和 HIIT 计划),或灵活性/柔韧性训练(请使用灵活性训练计划)。
youtube-script-writer
Writes retention-engineered long-form YouTube scripts on a hook/body/payoff architecture - a first-30-seconds hook that survives the retention cliff, a beat-sheet body with mini-hooks and bridges, timed pattern interrupts, and a payoff that over-delivers - with B-roll and editing cues marked inline. Use when someone says "script my next video", "my retention graph dies at the start", "tighten this draft", or "outline a video on X". Do NOT use for the 3-second opener of TikTok, Reels, or Shorts - use video-hook-writer instead; do NOT use for spoken voiceover synced to on-screen tutorial actions - use narration-script instead; do NOT use for a sales demo built on a buyer's discovery notes - use demo-script instead; do NOT use to design the thumbnail and title - use thumbnail-concept instead.
win-back-campaign
Designs segmented win-back email sequences for dormant users - dormancy tiers, a three-beat sequence with a single proportional incentive, and a suppression rule - and measures success by re-activation, not opens. Use when someone says "our users went quiet", "design a win-back campaign", "how do I re-engage lapsed customers", "should we email people who haven't logged in for months", or lifecycle messaging has stopped landing with a lapsed segment. Do NOT use for recovering an unfinished checkout - use abandoned-cart-sequence instead. Do NOT use for diagnosing and fixing why active customers cancel - use churn-reduction instead. Do NOT use for building always-on onboarding or nurture flows - use email-drip-builder instead.
wikipedia-style
Writes neutral, verifiable, encyclopedic prose following Wikipedia's core content policies and manual of style - NPOV, inline citations, notability-backed claims, and a strict ban on promotional language. Use when someone asks "write this like a Wikipedia article", "draft a Wikipedia page for this company or person", "make this neutral and encyclopedic", or "why does my article draft keep getting rejected". Do NOT use for persuasive or opinion writing - use op-ed-writer instead; for thesis-driven academic papers, use academic-essay; for verifying claims in existing text, use fact-checker.
whitepapers
Structures and writes authoritative B2B whitepapers - a standalone executive summary, a data-sized problem statement, methodology, evidence-driven findings, recommendations, and a soft call to action - flagging every claim that needs a real citation. Use when a marketer asks "write a whitepaper on this topic", "turn our research into a lead-gen asset", "make this credible enough for executives", or a draft reads like a product pitch instead of analysis. Do NOT use for customer-story-driven proof - use case-study-builder instead; for policy audiences, use policy-brief.
white-space-analysis
Finds unmet market gaps by ranking jobs-to-be-done on importance versus satisfaction, overlaying them against current solutions and workarounds, and validating that each gap is real, winnable, and worth winning. Use when someone asks "where are the gaps in this market", "what's underserved here", "where should we play next", or is hunting for product, feature, or market-entry opportunities. Do NOT use for profiling competitors and building battle cards - use competitive-intelligence instead; for putting a dollar figure on an identified opportunity - use market-sizing instead; for extracting jobs-to-be-done from raw customer interviews - use jtbd-extractor instead.