---
description: Use when you want to reverse-engineer a model's working signature (narration, authoring, decision style) from local Claude Code transcripts on a machine that ran it — e.g. recreate the Fable corpus/KB/mode-skill on another PC, mine "what would Fable do", or build an imitation-mode skill for any model. Triggers on "extract fable", "mine fable from logs", "rebuild the fable corpus", "redo the fable analysis here".
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent
---


# Extracting a model's signature from Claude Code logs

audience: AI coding agents first. Optimize for activation, not prose. Do not prettify into narrative.

**Core principle: mine behavior + artifacts, never cognition. Measure, don't assert. Validate matched-pair or
don't claim.** This rebuilds the Fable corpus + KB + mode-skill from a machine's own transcripts. The pipeline
scripts live in `pipeline/` next to this file. Run them in order; read the lessons before believing any number.

## §0 — What IS and ISN'T mineable (honesty anchor — read first)

Four layers, three epistemic statuses. Tag every claim with its status; never let one layer's evidence
carry another's claim.

| Layer | Status | Source / rule |
|---|---|---|
| Behavior + narration (openers, tool cadence, word counts) | **OBSERVED [MEASURED]** | text blocks — obey it |
| Authored `.md` docs (what it Wrote) | **OBSERVED [MEASURED]** | `Write` content — but these are *decision-records*; the model's terse style **strips the "why"** (no rejected-alternative enumeration), so the deliberation is *not* in them |
| **Strategy STRUCTURE** (how it decomposed, what got a falsification test, where it scoped, ordering) | **INFERABLE [INFERRED]** — falsifiable by reproduction, **never observed** | inferred by an ultrathink pass over the corpus; a **hypothesis** until a matched-pair run reproduces it |
| Live chain-of-thought / actual deliberation | **UNOBSERVABLE** | `thinking` blocks are **0-char (encrypted)**. Hidden *twice*: encrypted here, *and* omitted from the terse docs by design |

`analyze.py` proves the bottom row every run (counts thinking blocks → 0 chars). **The boundary that matters:**
an ultrathink pass can **infer strategy *structure* from artifacts** — it cannot **recover *cognition***. Those
are different claims and only the weaker one is true. You reconstruct how it *acts, writes, and (inferred)
structures work* — never how it *thinks*. Label any strategy model `[INFERRED]` and gate it on reproduction
(§"Inferring strategy"); an unreproduced strategy model is a hypothesis, not a finding.

## Hard precondition — this only works where the model already ran

You cannot extract a model from a machine that never ran it. Input is **this machine's own local transcripts**
(`~/.claude/projects/**/*.jsonl`). There is no portable/network corpus to pull. **Step 1 gates on this:**

```
grep -rl claude-fable-5 ~/.claude/projects | head     # empty -> STOP. Nothing to extract here. Say so.
```

## Pipeline — run in order

Set the model once: everything keys off it (default `claude-fable-5`).

1. **Gate.** `grep -rl <MODEL> ~/.claude/projects` — empty → stop honestly, report "no <MODEL> history on this machine".
2. **Discover the signature.** `python3 pipeline/analyze.py --model <MODEL> [--baseline <OTHER>]`
   — prints the behavioral signature vs a baseline + the honesty probe. This is the analysis; read §"Reading the numbers".
3. **Extract the corpus.** `node pipeline/extract.mjs --model <MODEL> [--out ~/.claude/fable]`
   — writes `corpus/<project>/*.md` (authored docs), `narration/excerpts.md` (secret-scrubbed terse lines),
   `manifest.json`, `index.md`. **Never copies raw transcripts.**
4. **Build the KB.** `python3 pipeline/build_kb.py --root ~/.claude/fable` — SQLite FTS5 index (`fable.db`)
   for "what would <MODEL> do" retrieval.
5. **Query precedent** (used by the resulting mode-skill): `python3 pipeline/query_kb.py "<situation keywords>"`.
6. **Infer strategy structure (contrastive, ultrathink)** — see §"Inferring strategy". First
   `node pipeline/contrast.mjs --model <MODEL> --baseline <OTHER>` to mine MODEL-minus-baseline fossils into
   `strategy/readlist.md`; then dispatch ONE deep-reasoning agent over that read-list (deltas, not whole docs)
   to emit an `[INFERRED]` model. The signal is *divergence from baseline* — what it did that the baseline never would.
   **Save the agent's `[INFERRED]` model verbatim to `<out>/strategy/inferred.md`** — step 7's generator injects it.
6b. **Mine the verification profile (§E) — Fable-distinctive deltas ONLY.** Re-measure marker density:
   `node pipeline/verify_scan.mjs --root ~/.claude/fable` — prints PRESENT counts + an absence wall that flags
   DRIFT in the honesty notes (e.g. a stale scan once wrongly reported `assert(`=0; live corpus has ~200, so it is
   B6's runnable-acceptance idiom, not absent — the wall catches exactly that). Counts are ABSOLUTE (no baseline
   corpus) → they gate the honesty notes, they do NOT prove distinctiveness. Synthesize ONLY the contrastive/odd
   findings (the move-grammar verify-after-write contrast; an authored idiom a baseline wouldn't write) into
   `<out>/verification/profile.md` with epistemic tags + kill-tests; leave generic markers (gates, checkboxes,
   grep/curl) to §B. Step 7's generator injects this into §E. Do NOT pad §E with generic verification advice — that
   dilutes the imitation signal (the whole point of MODEL-minus-baseline).
7. **Generate the mode-skill — deterministic, do NOT hand-author.** Run `node pipeline/write_skill.mjs`. It ALWAYS
   create-or-overwrites `~/.claude/skills/fable/SKILL.md` from the canonical template (`templates/fable.SKILL.md.tmpl`),
   injecting the live `analyze.py` signature, the step-6 `strategy/inferred.md`, and the step-6b
   `verification/profile.md`, and copies
   `query_kb.py`/`measure_agent.py`/`build_kb.py` into the skill's `kb/` for self-containment. Never
   skip-because-it-exists — the overwrite is the point. **The canonical content lives in the template**: the
   validated levers (opener-form, terseness) are carried matched-pair assets — edit the TEMPLATE to change a rule,
   never the generated copy (a re-run clobbers it). Template structure already encodes this skill's two specs:
   §"Reading the numbers" (lead with the validated lever; tag frequency + strategy aspirational) and §"Validation"
   (the honesty anchor).
   - **Scope: fable-only.** The generator hardcodes the fable template/frontmatter/skill-dir and carries fable's
     matched-pair levers; it refuses `--model` other than `claude-fable-5` (those numbers are fable's, not portable).
     Generalizing to another model = parameterize template name + frontmatter + skill-dir AND drop the carried
     levers AND re-run step 8 per model. `--model`/`--baseline` only retune which transcripts fable's signature is
     measured over.
   - **Verify-by-diff:** after a live generate, `node pipeline/write_skill.mjs --dry /tmp/x` then
     `diff ~/.claude/skills/fable/SKILL.md /tmp/x/SKILL.md` should be **empty** (same template + inputs) — modulo
     live-signature drift if the corpus grew. A non-empty structural diff means real drift; investigate.
8. **Validate matched-pair** (see §Validation) with `pipeline/measure_agent.py`. No validation → no claim.

## Reading the numbers — which signals transfer, which are noise (DO NOT skip)

Hard-won from building the `fable` skill. A prompt can move some signals on another model; others are
weights-bound and will NOT move — claiming they do is the #1 failure.

| signal (`analyze.py`/`measure_agent.py`) | what it is | transfers via prompt? |
|---|---|---|
| **self-opener %** + **median words** | opener FORM (result-first, terse) | **YES — the validated lever.** Target this. |
| tool:text ratio, prose-emission % | narration FREQUENCY (how often it stops to talk) | **NO — weights-bound.** Aspirational only. |
| median doc chars | authoring depth | structural — terseness scales docs down, never to a stub |
| move-order grammar (`contrast.mjs` §A) | tool-sequence n-grams vs baseline | **OBSERVED [MEASURED]** — but divergence = strategy + deployment-context; down-weight context artifacts |
| inferred strategy structure (§"Inferring strategy") | decomposition / scoping / ordering disposition | **LEAST likely to transfer** — same class as tool:text. Ship `[INFERRED, UNVALIDATED]`, not as a win. |

## Inferring strategy — the contrastive fossil method (the highest-overclaim-risk step)

You **can** infer a model's strategy *structure*; you **cannot** recover its cognition (§0). Do NOT do this by
bulk-reading whole docs — that bloats context and regresses to the generic. The distinctive signal is **MODEL
minus BASELINE**: where the target diverges from a strong baseline (default Opus) is what's distinctive — *"what
did it do that the baseline never would."* You are the control; the diff exposes blind spots you can't introspect.

**Mechanical first, agent second.** `contrast.mjs` does the expensive locating cheaply, so the deep agent reads
curated DELTAS, never documents:

```
node pipeline/contrast.mjs --model claude-fable-5 --baseline claude-opus-4-8 --out ~/.claude/fable --top 8
```

It emits `strategy/readlist.md` with three fossil types, ranked by signal (never random):

1. **Revision deltas — the cleanest signal.** A `.md` Written N times = N−1 decision deltas (add/cut/reorder).
   The thinking is encrypted, but its *consequences* (the edits) are logged. Context-controlled (same doc), so
   this is the load-bearing layer. Read each step as a decision: "why this edit, would the baseline make it?"
2. **Move-order grammar — OBSERVED [MEASURED].** Tool-call 3-grams over the session timeline (not per turn),
   reported as lift over baseline. **Caveat: divergence = strategy + deployment-context.** High-lift hits like
   `StructuredOutput`/`TaskUpdate` are *where the model ran* (workflows, task-tracking), not strategy — down-weight
   them. Trust within-task move ratios (e.g. read/Bash interleaving), not context artifacts. Let the data falsify
   priors: here it killed "Fable verifies more" (verify-after-write 38% vs baseline 62%).
3. **Archetype pairs + marker density.** Same-type docs (plan/spec/review) per model → diff for what MODEL
   *omits*; falsification-marker ranking → which docs carry the acceptance skeleton.

**Then** dispatch ONE deep-reasoning agent over `strategy/readlist.md` (this is where `ultrathink` earns its
place — reason hard over the deltas, do not bulk-read corpus). Agent prompt MUST contain, verbatim in spirit:

```
ultrathink. Infer claude-fable-5's STRATEGY STRUCTURE from the contrastive fossils in strategy/readlist.md —
NOT its cognition. Its reasoning is unobservable (thinking 0-char/encrypted) and its docs strip the "why" by
style; you infer structure from the MODEL-minus-BASELINE deltas, not from deliberation.
The signal is divergence from the baseline. For each candidate rule: cite the fossil (a revision step, a move
n-gram, an omission), and judge strategy vs deployment-context artifact — down-weight context.
Output an [INFERRED] model. FORBIDDEN: any claim you "reverse-engineered its reasoning / know how it thinks".
End with the reproduction test that would validate or kill each rule (matched-pair, §Validation). Read at most
1–2 named docs only to confirm a specific omission; never bulk-read.
```

**The inferred model is `[INFERRED, UNVALIDATED]`** until a matched-pair run shows a model steered by it
reproduces Fable's artifact structure on a held-out task (n≥3, non-overlapping). Without that it is a hypothesis —
the reproduction gate is the only thing separating inference from confabulation. By this session's evidence,
expect most rules **not** to transfer (disposition is the hardest layer); ship tagged aspirational.

## Validation — the discipline that makes a claim honest

- **Matched-pair RED→GREEN.** Same task, baseline model with NO skill (RED) vs with the skill (GREEN). Measure both
  with `pipeline/measure_agent.py <transcript.jsonl>`.
- **n ≥ 3 per condition.** A single run proves nothing — these metrics are noisy.
- **Report only signals whose GREEN spread does NOT overlap baseline.** If GREEN ranges 1.8–4.0 and baseline is 2.4,
  that signal did not move — say so. (This exact error — "2.4→4.0 from one run" — shipped once and was deleted.)
- **Claim only opener-form + terseness** unless a frequency metric genuinely separates. It usually won't.

## Security — non-negotiable

- **Never copy raw transcripts into the corpus.** They are not model-authored and carry secrets/PII. `extract.mjs`
  only emits authored `.md` docs + scrubbed narration.
- **Secret-scrub every excerpt** before it leaves a transcript (`extract.mjs` filters hex/tokens/keys/emails/PEM).
  Verify: `grep -nE '(sk|ghp|xox)[-_][A-Za-z0-9]{8,}|[A-Fa-f0-9]{32,}|password|api[_-]?key' <out>/narration/excerpts.md`.

## Common mistakes / red flags — STOP

- "I reverse-engineered its reasoning / I know how it thinks" → FALSE. Cognition is encrypted (§0). You may
  *infer strategy STRUCTURE* from artifacts (§"Inferring strategy") and label it `[INFERRED]` — that is a
  different, weaker, falsifiable claim. Never collapse the two.
- Shipping an inferred strategy model as a finding without a reproduction match → it's a hypothesis until then.
- Targeting narration-frequency (tool:text, prose %) as the headline imitation metric → it's weights-bound; won't move.
- Claiming success from one GREEN run → n≥3, non-overlapping spread, or no claim.
- Pasting verbatim transcript samples as exemplars without scrubbing → secret leak.
- Mining only chat narration, ignoring the authored `.md` docs → you skipped the richest layer (`extract.mjs` gets both).
- Running on a machine that never ran the model → the gate returns empty; stop, don't fabricate.

## Validate this skill

Re-run any model: `python3 pipeline/analyze.py --model <X>`. The honesty probe must show thinking-chars = 0.
The resulting mode-skill is validated by its own matched-pair `measure_agent.py` runs, not by assertion.

$ARGUMENTS

Bundled assets for this command live in `/home/user/.claude/command-assets/extracting-fable/`. Reference them BY ABSOLUTE PATH — any relative path in the text above resolves there.
