agentleFS
qyndexqyndex/claude-init/.claude/skills/characterize/SKILL.md

characterize

Skill2 starsChanged 5 months agoMITView on GitHub

Write characterization (approval/golden-master) tests that pin the CURRENT behavior of untested legacy code BEFORE it is changed. The "no tests = no writes" safety net for brownfield adoption — distinct from tdd-loop (which drives NEW behavior red→green). Use during /adopt Phase 4, or whenever a task must touch a file flagged in .claude/state/adopt/uncharacterized-paths.txt.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

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

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Capture current output (the golden master): Run the code against representative inputs (include real production-shaped data + edge cases …

    line 31
  2. Very specific:

    Record the ledger: Write verify/<date>/<task>/green.log (it passed) so the TDD-ledger gate is satisfied.

    line 40
  3. Distinctive:

    If the task ADDS behavior (not modifies), use Feathers' Sprout Method/Class: write the new logic in a fresh, fully-TDD'd function/class and …

    line 52
  4. Distinctive:

    Document reality, including bugs: If the legacy code returns the wrong total, the characterization test asserts the wrong total.

    line 61
  5. Distinctive:

    Smallest seam: Don't refactor broadly to make code testable.

    line 64

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 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

Characterize

Pin what the code does today — not what it should do — so you can change it without silently breaking it. From Michael Feathers, Working Effectively with Legacy Code: a characterization test documents existing behavior; you write it, THEN change the code, and the test tells you if you altered behavior you didn't mean to.

This is the inverse of tdd-loop:

tdd-loopcharacterize
Goaldrive NEW behaviorfreeze EXISTING behavior
"red" meansthe feature isn't built yet(n/a — there was no test)
Oraclethe spec's acceptance criteriathe code's current output
Whengreenfield / new featurebrownfield / untested legacy

The loop

  1. Find a seam. Identify where you can observe the code's output without changing it — a pure function's return, an HTTP handler's response, a CLI's stdout, a rendered DOM. If there's no seam, introduce the minimum one (extract a function, inject a dependency) — that itself is a behavior-preserving refactor; do it tiny and commit separately.
  2. Capture current output (the golden master). Run the code against representative inputs (include real production-shaped data + edge cases from the hotspots list). Record exactly what it returns/emits — bugs included. You are documenting reality, not correctness.
  3. Write the test asserting that captured output. Name it so the gate recognizes it: <name>.characterization.test.<ext> (JS/TS) or test_<name>_characterization.py (Python), co-located or under the repo's test root. Use the repo's existing test runner (see AGENTS.md / atlas).
  4. Run it — it must PASS immediately (it asserts what the code already does). A failing characterization test means your captured oracle is wrong; fix the capture, not the code.
  5. Record the ledger. Write verify/<date>/<task>/green.log (it passed) so the TDD-ledger gate is satisfied. There is no red.log requirement for characterization — note "characterization (no prior test)" in the task instead.
  6. Now you may change the code. Make your change; re-run the characterization test. If it fails, you changed observable behavior — decide deliberately: update the golden master (with a one-line rationale) or revert.
  7. Remove the path from the manifest. Once a flagged-legacy file has a characterization test, delete its glob (or the file) from .claude/state/adopt/uncharacterized-paths.txt so autopilot and verify.sh stop blocking it.

For NEW features in untested legacy: sprout, don't touch

If the task ADDS behavior (not modifies), use Feathers' Sprout Method/Class: write the new logic in a fresh, fully-TDD'd function/class and call it from the legacy code with the smallest possible edit. The legacy body stays untouched — so you never owe it a characterization test, and the new code gets normal tdd-loop red→green rigor.

Hard rules

  • Characterize before you change. No tests = no writes (verify.sh enforces; SKIP_CHAR_GATE=1 is a human-only override, never an autopilot one).
  • Document reality, including bugs. If the legacy code returns the wrong total, the characterization test asserts the wrong total. Fixing the bug is a separate, later task with its own spec — now made safe because the test will flag the behavior change.
  • Smallest seam. Don't refactor broadly to make code testable; introduce the minimum seam, commit it alone, then characterize.
  • Hotspots first. Characterize the churn×LOC hotspots from ADOPTION-REPORT.md before the long tail — that's where factory-driven change is most likely and most dangerous.

References

  • Michael Feathers, Working Effectively with Legacy Code (characterization tests, seams, sprout/wrap)
  • docs/research/brownfield-onboarding.md (the adoption research brief)
  • .claude/skills/tdd-loop/SKILL.md (the greenfield counterpart)
  • .claude/commands/adopt.md (Phase 4 invokes this)

Download now (ZIP)

Does it work?

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

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

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.