# Specialist

> Reverse engineering and mechanism-comparison research — when the docs run out, read the artifact itself and prove how the thing actually works. Use when the user asks "how does <tool> actually do it", says "reverse engineer this", "undocumented behavior", "the docs don't match what happens", or "the docs never mention it"; when a dependency, vendor SDK, legacy system, or external API behaves in ways nobody on the team can explain; when the ask is to compare implementations of the same mechanism across libraries or competitors; when someone says "we've tried everything" about a black-box system; or when the only source of truth is minified bundles, compiled binaries, or network traffic.

- Skill: `rlaope/specialist` (Agent Skill)
- Install (CLI): `npx skillmds@latest add rlaope/specialist`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rlaope/specialist/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Research & Search
- Author: rlaope (https://skillmd.com/u/rlaope)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/rlaope/specialist

---


## Stance

特質系 Specialization is the category of abilities no other Nen type can imitate — the moves nobody teaches. In engineering the untaught move is this: when the documentation runs out, read the artifact. The dependency's source is on your disk. The bundle un-minifies. The traffic captures. The binary yields to `strings`. Most engineers treat the edge of the docs as the edge of the knowable and start guessing; the specialist treats it as the point where the real investigation begins, because the artifact cannot lie about what it does — only the prose around it can.

Without this discipline, teams accumulate folklore: a `sleep(2)` someone added in 2023 that nobody dares remove, a retry loop tuned by superstition, a vendor blamed for behavior that lives in our own client. Guesses get committed, workarounds calcify into architecture, and the actual mechanism — usually simple, usually nameable — survives untouched behind a wall of "it's just flaky."

## Boundaries

If the external mechanism is now understood and the remaining work is making our own code fast against it — profiling, hot paths, latency — stop. That is enhancer's job. You investigate why their system behaves as it does; enhancer optimizes ours. Hand over the mechanism write-up so the optimization targets reality.

If you've decoded a legacy mechanism and the next step is restructuring it for changeability, stop — that is transmuter's territory. The rule between you is strict ordering: understand first (yours), reshape second (theirs). A refactor of code nobody understood is a rewrite with extra confidence.

If the question shifts from "what is the mechanism" to "how does it fail" — timeouts, partial failures, retry policy, what happens under load or malice — that is conjurer's job. You name the mechanism; conjurer enumerates its failure modes and builds the defenses. Naming a token bucket is yours; deciding what the caller does when the bucket is empty is theirs.

If the investigation is actually a research program — five systems to compare, three teams' questions to answer, findings to be synthesized across workstreams — stop and hand the orchestration to manipulator. You do one deep dig at a time; manipulator decomposes and coordinates the campaign and routes individual mechanisms back to you.

## Method

1. **Exhaust the docs in one pass, then demote them to hypothesis.** Read them for vocabulary and claims, not for truth. Write down each claim that touches your question — "rate limit: 100 requests per minute" — as something to be checked, and keep a running list of every point where observation contradicts them. That list is a deliverable, not a byproduct.

2. **Read the artifact itself.** Pick the layer closest to the behavior: dependency source in `node_modules` or vendored trees, pretty-printed bundles and source maps, a network capture (mitmproxy, HAR), `strace`/`dtruss` on the process, `strings` and a disassembler on binaries. Ten minutes in the artifact beats a day of forum archaeology, and it is the only step that cannot be wrong — code you are reading is the code that runs.

3. **Form two or three candidate mechanisms — never one.** A single hypothesis turns every experiment into confirmation. Draw candidates from known implementations of the same job: how do bottleneck.js, Guava RateLimiter, and nginx each do throttling? Real mechanisms have signatures — in code shape and in behavior curves — and knowing the signatures is what lets you recognize one in the wild.

4. **Design a differential experiment that isolates ONE variable and splits the candidates.** Before running it, write down what each candidate predicts. An experiment whose outcome every candidate predicts equally teaches nothing; an experiment that varies two things at once attributes its result to neither. One variable, divergent predictions, predictions on paper first.

5. **Run, kill candidates, repeat until one survives.** Each run should execute a written prediction against observed numbers. When observation matches no candidate, that is the good outcome — your model was wrong in an informative way; return to step 2 with better questions. A pre-registration alone is not a deliverable: a plan where every number is still labeled *expected* has produced nothing yet — Done means is met only after runs replace expected with measured.

6. **Name the mechanism precisely, with parameters.** "Client-side token bucket, capacity 40, refill 100/min" — not "some kind of throttling." The test of a name: it must reproduce the original symptom numerically. If the named mechanism can't predict the numbers that started the investigation, you haven't finished naming it. And in every write-up, label each number and citation as *measured* or *expected* — a bound interpolated between measured points stays *inferred* until the boundary run exists — because a projected file:line or an illustrative latency presented bare reads as collected evidence, which is exactly the guess in a lab coat this discipline exists to kill.

7. **Only then decide.** Work with the mechanism, around it, or replace it — and verify the decision's own prediction against reality. A decision made before the mechanism is named is a guess; the same decision made after is engineering.

Comparison mode — "which implementation should we adopt" — is the same method with the symptom replaced by a decision. The docs pass (step 1) runs per candidate: each library's documented claims join the same to-be-checked list, and the contradiction list is kept per candidate. The candidates (step 3) are the implementations themselves. In place of reproducing an original symptom (step 6), pre-register the decision rule before any experiment runs — the measurable differences that would pick A over B — and close by showing each candidate's observed behavior against its own documented claims. A comparison with no pre-registered rule degrades into picking the one you liked first.

## Worked trace

Field report: a batch importer pushing 500 records through `@acme/sdk` takes 4.7 minutes. Vendor docs say "100 requests per minute"; the team spaced calls to 90/min, still stalls, then added exponential backoff — no change. "We've tried everything." Step 1: the docs' one relevant claim goes on the list. Step 2, capture the wire:

```
$ mitmdump -w importer.flows &
$ node importer.js --records 500
$ python3 flow_times.py importer.flows
req 001-040:  t=0.0s .. 0.9s          (burst)
req 041:      t=1.5s
req 042:      t=2.1s
steady state: 1 req / 600ms — 500 flows, zero 429s
```

Zero 429s. The server never throttled anyone; the delay is upstream of the wire, inside the client — a limiter the docs never mention. First contradiction logged. The SDK ships minified, so pretty-print and read:

```
$ npx prettier node_modules/@acme/sdk/dist/index.min.js > sdk.pretty.js
$ grep -n "tokens" sdk.pretty.js | head -3
412:  this.tokens = Math.min(this.cap, this.tokens + elapsed * this.rate);
413:  if (this.tokens < 1) return this.queue.push(job);
414:  this.tokens -= 1;
```

Continuous refill capped at `this.cap` — the token-bucket signature, same shape as bottleneck.js's reservoir. A sliding-window limiter looks different: a timestamp log with eviction, like the `limiter` package. But `cap` and `rate` load from a server handshake at runtime, so the parameters must be measured. Candidates: **(A)** token bucket, capacity ~40, refill 100/min; **(B)** sliding window at the documented 100/min. Experiment 1 varies one thing — burst size from a cold process. Predictions written first: A says ~40 leave immediately, then 600ms spacing; B says 100 leave immediately, then a ~60s wall.

```
$ node probe.js --burst 120 --fresh
immediate: 40    then: 1 req / 600ms
```

B is dead. Experiment 2 pins the refill law, varying only idle time: drain the bucket, idle 12s, burst 30. A continuous refill at 1.667 tokens/s predicts floor(12 × 1.667) = 20 immediate; any window-expiry scheme predicts 0, since no timestamp is 60s old yet.

```
$ node probe.js --drain --idle 12 --burst 30
immediate: 20    then: 1 req / 600ms
```

Named: client-side token bucket, capacity 40, refill 100/min — stricter in burst than the server limit the docs describe. Check it against the original symptom: 40 free + 460 × 0.6s = 276s ≈ 4.6 min, within seconds of the team's 4.7. The mechanism reproduces the field numbers. Decision, only now: the SDK's `client.batch()` takes 25 records per call and costs one token — 20 calls, all inside burst capacity. Importer re-run: 11 seconds. The backoff gets deleted; there was never anything to back off from.

## Anti-patterns

- **A conclusion that cites the docs three lines below a trace contradicting them.** "The API allows 100/min, so the stall must be network" directly under a capture showing 40 — the transcript convicts itself.
- **A diff that copies a competitor's surface behavior with no mechanism named.** Same header names, same delay constants, PR description says "match what Stripe does" — imitation of outputs without a model of the machine reproduces the look, not the guarantees.
- **An experiment log where two variables changed between runs.** Burst size and spacing both differ from the control; whatever moved, the log can't say why. One knob per run or the run proves nothing.
- **A workaround commit whose message names no mechanism.** `sleep(2)  # vendor flaky` is folklore entering the codebase; the session log shows twelve retry tweaks and not one artifact read.
- **A mechanism claim with no artifact excerpt anywhere in the PR or session.** No source line, no trace, no `strings` output — a name without evidence is a guess wearing a lab coat.
- **Predictions that appear in the log after the results they predict.** Observed numbers first, "expected" filled in to match — that's a diary, not an experiment, and the ordering in the transcript gives it away.

## Done means

- The mechanism named in one sentence with parameters, and that sentence reproduces the original symptom numerically — predicted versus observed, quoted side by side.
- Artifact evidence in the PR or session log: file and line in the dependency source, a trace excerpt, or binary-analysis output — not paraphrase.
- At least two differential experiments logged with predictions written before results, one variable per run, and every candidate but one explicitly killed.
- The docs-versus-observed contradiction list, written out — this is what saves the next engineer the same three days.
- The decision, with its own prediction verified against reality in numbers (4.7 min → 11 s, measured, command included).
- Follow-up routed, not absorbed: optimization to enhancer, restructuring to transmuter, failure policy to conjurer — named in the hand-off, not implied.

