Optimize Mock Data
Audit and normalize a set of related JSON (or JSONL) mock fixtures so
every file shares the same shape, formatting, and verbosity budget.
Pure shape work — never touches semantics or business values unless
the user explicitly asks for shrink.
This SKILL.md is a thin index. Detailed rules live in
rules/*.md and load on demand. Reusable Python scripts live in
scripts/*.py and run via Bash. Worked example output lives in
references/example-report.md.
Mode Detection
Parse $ARGUMENTS as <mode> <path> [flags] where <mode> is one of
analyze, normalize, shrink, trim. If the first token is a path
(starts with /, ./, or ~) treat it as <path> and default
<mode> to analyze.
| Mode |
Default |
Trigger |
Side effect |
analyze |
yes |
analyze, audit, check, report, or first arg is a path |
Read-only report |
normalize |
|
normalize, fix, format, reorder |
Rewrites files |
shrink |
|
shrink, shorten, truncate-strings |
Rewrites files (string truncation) |
trim |
|
trim, trim-arrays, cardinality, reduce-arrays, compact, slim |
Rewrites files (array entry reduction; never modifies strings) |
State the detected mode, target path, and file count in one line
before continuing:
Mode: analyze
Target: components/ui/src/agent0/mocks/ (22 files)
Workflow
A four-phase pipeline. Each phase has a gate; do not proceed until it
passes.
Phase 0 — Resolve corpus
- If
<path> is a directory: glob **/*.json and **/*.jsonl
(exclude node_modules/, dist/, .next/, coverage/).
- If
<path> is a single file: include it plus all siblings
matching the same basename pattern — e.g.
invoke-agent-artifacts-dashboards-4.json expands to
invoke-agent-artifacts-dashboards*.json in the same directory.
The user almost always means "this file and its peers".
- If fewer than 2 files resolve, halt — single-file optimization is
npx prettier --write territory, not this skill's job.
State the resolved corpus before continuing:
Resolved 4 peer files for invoke-agent-artifacts-dashboards-4.json:
- invoke-agent-artifacts-dashboards.json
- invoke-agent-artifacts-dashboards-2.json
- invoke-agent-artifacts-dashboards-3.json
- invoke-agent-artifacts-dashboards-4.json
Phase 1 — Shape extraction
Run scripts/shape.py on each file. Each file
gets a deterministic shape fingerprint — a sorted, recursive type
signature where:
- Object keys are sorted alphabetically.
- Values become their type (
string, number, boolean, null,
object{...}, array[T]).
- Heterogeneous arrays collapse to
array[union[T1, T2, ...]].
- Leaf string values are not in the signature — only types.
See rules/shape-extraction.md for the
algorithm, the optionality rules, and the JSON Schema mapping.
Phase 2 — Drift detection
Run scripts/diff-shapes.py over every
fingerprint pair. Cluster files by fingerprint. For each cluster:
- Majority cluster (the most common shape) becomes the reference.
- Outlier clusters are reported as drift, scored by edit distance.
- For each outlier, list the key paths that differ (
messages[].userId
present in 18 files, missing in 4).
Output the drift report — see
rules/drift-detection.md for the
report format and severity rubric. The exact format used in
analyze mode is in
references/example-report.md.
Phase 3 — Apply (mode-gated)
| Mode |
What Phase 3 does |
analyze |
Stop after Phase 2. Emit the drift report. Do not write files. |
normalize |
Run scripts/normalize.py per file: 2-space indent, sorted keys (configurable), trailing newline, LF line endings. Optional --fill-missing null adds missing-but-expected keys as null. |
shrink |
Run scripts/shrink.py per file: truncates string fields above the threshold per rules/shrink-policy.md. Refuses to shrink fields named id, hash, actionId, threadId, userId, or anything matching *Id$. |
trim |
Run scripts/trim.py per file: caps the length of arrays of data points (webEvents, logRecords, dataPoints, series, attributes, catalog, …) nested inside artifacts.* subtrees, preserving order. Strict allowlist of parent keys — no default fallback, so structural arrays (panels, widgets, queries) stay intact. Never modifies any string under any condition — content and every other string field round-trip byte-identical. Preserves source indentation. See rules/trim-policy.md. |
After every write, re-parse the file to verify it is still valid JSON
and re-run Phase 1 against the corpus. If post-rewrite drift is worse
than pre-rewrite drift, revert all writes and halt — this means
the scripts have a bug or the policy is wrong for this corpus.
Required Reading by Phase
Load on demand — do not preload.
Worked output examples in
references/example-report.md are
optional — load only when the user asks "what does the report look
like?".
Reusable scripts
All scripts are pure Python 3 with stdlib only. No pip install
required. They read from stdin or paths, write to stdout, and exit
non-zero on shape-validation failure. Run them from the repo root.
Invocation pattern (run from the repo root, scripts are relative to
this skill directory):
SKILL_DIR="$HOME/.claude/skills/optimize-mock-data"
python3 "$SKILL_DIR/scripts/shape.py" path/to/mock.json
python3 "$SKILL_DIR/scripts/diff-shapes.py" path/to/mocks/
python3 "$SKILL_DIR/scripts/normalize.py" --in-place path/to/mock.json
python3 "$SKILL_DIR/scripts/shrink.py" --max-string 200 --in-place path/to/mock.json
python3 "$SKILL_DIR/scripts/trim.py" --in-place path/to/mock.json
Core Principles
- Shape, not semantics. This skill normalizes structure and
formatting. It never edits business values (the
content of a
message, the name of a thread) unless shrink is explicit.
- Majority wins. When clusters disagree, the largest cluster is
the reference. Tie-breaker is the most recently modified file.
- Round-trip safety. Every rewritten file must parse back into
the same Python object after canonicalization. If not, revert.
- Idempotent. Running
normalize twice changes nothing the
second time. Running shrink twice with the same threshold
changes nothing the second time.
- No network, no deps. Scripts use stdlib only so they run in
any sandbox, CI, or pre-commit hook.
Anti-patterns
- Editing the value of a field (
"role": "human" → "role": "user")
in normalize mode. Shape work only.
- Sorting array elements. Arrays are ordered; only object keys
are sorted.
- Truncating an
*Id or hash field. Identifiers are load-bearing
for fixture lookups even in tests.
- Treating a
.jsonl file as a single JSON document. JSONL is
newline-delimited; each line is a separate fingerprint.
- Inferring a schema from one file. Need ≥ 2 to detect drift.
- Trimming arrays outside
artifacts.*. Top-level conversation
arrays ($.messages, $.thread.*) are off-limits to trim.
trim only descends into artifacts.* subtrees.
- Trimming a structural array (
panels, widgets, queries).
Those describe the dashboard's shape, not data points. The
allowlist is strict by design — do not add a default fallback
budget that would catch them.
- Modifying any string inside
trim. The mode is array-cardinality
only. content, panel descriptions, tool arguments — every
string round-trips byte-identical. Use shrink if string
truncation is wanted.
Definition of Done
Diagnosable
This skill declares a diagnostic surface at
rules/diagnostic-surface.md — phase
model, failure taxonomy (F-novel-seeded), existing-guards table, and
hard invariants. Run /create-skill diagnose optimize-mock-data after
a failed or unsatisfactory run to get a confidence-gated unified diff
that hardens the skill against the same failure class.
1---2name: optimize-mock-data3description: Optimizes a directory of structurally-related JSON / JSONL mock fixtures by inferring a shared schema, detecting structural drift between files, normalizing formatting and key order, and optionally shrinking verbose payloads while preserving shape. Use when fixture files have grown inconsistent (mixed tabs / 2-space indent, reordered keys, fields present in some files but missing from others, megabyte-sized payloads), when adding a new mock that must match an existing set, or when preparing fixtures for a storage-cost-sensitive context. Four modes — `analyze` (default, read-only), `normalize` (rewrites files in place), `shrink` (caps verbose string fields), `trim` (reduces array cardinality without touching strings). Triggers on "optimize mock data", "normalize fixtures", "check mock structure", "audit mocks", "shrink test fixtures", "are these mocks consistent", "/optimize-mock-data".4license: MIT5---67# Optimize Mock Data89Audit and normalize a set of related JSON (or JSONL) mock fixtures so10every file shares the same shape, formatting, and verbosity budget.11Pure shape work — never touches semantics or business values unless12the user explicitly asks for `shrink`.1314> **This `SKILL.md` is a thin index.** Detailed rules live in15> `rules/*.md` and load on demand. Reusable Python scripts live in16> `scripts/*.py` and run via `Bash`. Worked example output lives in17> `references/example-report.md`.1819---2021## Mode Detection2223Parse `$ARGUMENTS` as `<mode> <path> [flags]` where `<mode>` is one of24`analyze`, `normalize`, `shrink`, `trim`. If the first token is a path25(starts with `/`, `./`, or `~`) treat it as `<path>` and default26`<mode>` to `analyze`.2728| Mode | Default | Trigger | Side effect |29| ----------- | ------- | ---------------------------------------------------------------------- | --------------------------------- |30| `analyze` | **yes** | `analyze`, `audit`, `check`, `report`, or first arg is a path | Read-only report |31| `normalize` | | `normalize`, `fix`, `format`, `reorder` | Rewrites files |32| `shrink` | | `shrink`, `shorten`, `truncate-strings` | Rewrites files (string truncation) |33| `trim` | | `trim`, `trim-arrays`, `cardinality`, `reduce-arrays`, `compact`, `slim` | Rewrites files (array entry reduction; never modifies strings) |3435State the detected mode, target path, and file count in one line36before continuing:3738```39Mode: analyze40Target: components/ui/src/agent0/mocks/ (22 files)41```4243---4445## Workflow4647A four-phase pipeline. Each phase has a gate; do not proceed until it48passes.4950| Phase | Name | Rule file | Gate |51| ----- | ----------------- | -------------------------------------------------------------------- | ------------------------------------------------- |52| 0 | Resolve corpus | — | Target resolved to ≥ 2 JSON files |53| 1 | Shape extraction | [`rules/shape-extraction.md`](./rules/shape-extraction.md) | Each file has a shape fingerprint |54| 2 | Drift detection | [`rules/drift-detection.md`](./rules/drift-detection.md) | Drift report produced (or "no drift") |55| 3 | Apply (mode-gated)| [`rules/shrink-policy.md`](./rules/shrink-policy.md) (shrink only) | Rewrites pass round-trip parse; `analyze` skips |5657### Phase 0 — Resolve corpus58591. If `<path>` is a directory: glob `**/*.json` and `**/*.jsonl`60 (exclude `node_modules/`, `dist/`, `.next/`, `coverage/`).612. If `<path>` is a single file: include it **plus all siblings62 matching the same basename pattern** — e.g.63 `invoke-agent-artifacts-dashboards-4.json` expands to64 `invoke-agent-artifacts-dashboards*.json` in the same directory.65 The user almost always means "this file and its peers".663. If fewer than 2 files resolve, halt — single-file optimization is67 `npx prettier --write` territory, not this skill's job.6869State the resolved corpus before continuing:7071```72Resolved 4 peer files for invoke-agent-artifacts-dashboards-4.json:73 - invoke-agent-artifacts-dashboards.json74 - invoke-agent-artifacts-dashboards-2.json75 - invoke-agent-artifacts-dashboards-3.json76 - invoke-agent-artifacts-dashboards-4.json77```7879### Phase 1 — Shape extraction8081Run [`scripts/shape.py`](./scripts/shape.py) on each file. Each file82gets a deterministic **shape fingerprint** — a sorted, recursive type83signature where:8485- Object keys are sorted alphabetically.86- Values become their type (`string`, `number`, `boolean`, `null`,87 `object{...}`, `array[T]`).88- Heterogeneous arrays collapse to `array[union[T1, T2, ...]]`.89- Leaf string values are **not** in the signature — only types.9091See [`rules/shape-extraction.md`](./rules/shape-extraction.md) for the92algorithm, the optionality rules, and the JSON Schema mapping.9394### Phase 2 — Drift detection9596Run [`scripts/diff-shapes.py`](./scripts/diff-shapes.py) over every97fingerprint pair. Cluster files by fingerprint. For each cluster:9899- **Majority cluster** (the most common shape) becomes the reference.100- **Outlier clusters** are reported as drift, scored by edit distance.101- For each outlier, list the **key paths** that differ (`messages[].userId`102 present in 18 files, missing in 4).103104Output the drift report — see105[`rules/drift-detection.md`](./rules/drift-detection.md) for the106report format and severity rubric. The exact format used in107`analyze` mode is in108[`references/example-report.md`](./references/example-report.md).109110### Phase 3 — Apply (mode-gated)111112| Mode | What Phase 3 does |113| ----------- | ----------------------------------------------------------------------------------------------------------------- |114| `analyze` | **Stop after Phase 2.** Emit the drift report. Do not write files. |115| `normalize` | Run [`scripts/normalize.py`](./scripts/normalize.py) per file: 2-space indent, sorted keys (configurable), trailing newline, LF line endings. Optional `--fill-missing null` adds missing-but-expected keys as `null`. |116| `shrink` | Run [`scripts/shrink.py`](./scripts/shrink.py) per file: truncates string fields above the threshold per [`rules/shrink-policy.md`](./rules/shrink-policy.md). **Refuses to shrink fields named `id`, `hash`, `actionId`, `threadId`, `userId`, or anything matching `*Id$`.** |117| `trim` | Run [`scripts/trim.py`](./scripts/trim.py) per file: caps the length of arrays of data points (`webEvents`, `logRecords`, `dataPoints`, `series`, `attributes`, `catalog`, …) nested inside `artifacts.*` subtrees, preserving order. **Strict allowlist of parent keys — no default fallback, so structural arrays (`panels`, `widgets`, `queries`) stay intact.** **Never modifies any string under any condition — `content` and every other string field round-trip byte-identical.** Preserves source indentation. See [`rules/trim-policy.md`](./rules/trim-policy.md). |118119After every write, re-parse the file to verify it is still valid JSON120and re-run Phase 1 against the corpus. If post-rewrite drift is worse121than pre-rewrite drift, **revert all writes and halt** — this means122the scripts have a bug or the policy is wrong for this corpus.123124---125126## Required Reading by Phase127128Load on demand — do not preload.129130| Phase | Files |131| ----- | ---------------------------------------------------------------------------------- |132| 1 | [`rules/shape-extraction.md`](./rules/shape-extraction.md) |133| 2 | [`rules/drift-detection.md`](./rules/drift-detection.md) |134| 3 | [`rules/shrink-policy.md`](./rules/shrink-policy.md) (shrink mode only), [`rules/trim-policy.md`](./rules/trim-policy.md) (trim mode only) |135136Worked output examples in137[`references/example-report.md`](./references/example-report.md) are138optional — load only when the user asks "what does the report look139like?".140141---142143## Reusable scripts144145All scripts are pure Python 3 with stdlib only. No `pip install`146required. They read from stdin or paths, write to stdout, and exit147non-zero on shape-validation failure. Run them from the repo root.148149| Script | One-liner |150| ------------------------------------------------------------ | ----------------------------------------------------------- |151| [`scripts/shape.py`](./scripts/shape.py) | Emit a JSON shape fingerprint for a file. |152| [`scripts/diff-shapes.py`](./scripts/diff-shapes.py) | Cluster files by fingerprint and report drift. |153| [`scripts/normalize.py`](./scripts/normalize.py) | Rewrite a file with canonical formatting + key order. |154| [`scripts/shrink.py`](./scripts/shrink.py) | Truncate verbose string fields above a configurable budget. |155| [`scripts/trim.py`](./scripts/trim.py) | Cap arrays of data points inside `artifacts.*` subtrees. Never modifies strings. |156157Invocation pattern (run from the repo root, scripts are relative to158this skill directory):159160```bash161SKILL_DIR="$HOME/.claude/skills/optimize-mock-data"162python3 "$SKILL_DIR/scripts/shape.py" path/to/mock.json163python3 "$SKILL_DIR/scripts/diff-shapes.py" path/to/mocks/164python3 "$SKILL_DIR/scripts/normalize.py" --in-place path/to/mock.json165python3 "$SKILL_DIR/scripts/shrink.py" --max-string 200 --in-place path/to/mock.json166python3 "$SKILL_DIR/scripts/trim.py" --in-place path/to/mock.json167```168169---170171## Core Principles1721731. **Shape, not semantics.** This skill normalizes structure and174 formatting. It never edits business values (the `content` of a175 message, the `name` of a thread) unless `shrink` is explicit.1762. **Majority wins.** When clusters disagree, the largest cluster is177 the reference. Tie-breaker is the most recently modified file.1783. **Round-trip safety.** Every rewritten file must parse back into179 the same Python object after canonicalization. If not, revert.1804. **Idempotent.** Running `normalize` twice changes nothing the181 second time. Running `shrink` twice with the same threshold182 changes nothing the second time.1835. **No network, no deps.** Scripts use stdlib only so they run in184 any sandbox, CI, or pre-commit hook.185186---187188## Anti-patterns189190- Editing the value of a field (`"role": "human"` → `"role": "user"`)191 in normalize mode. **Shape work only.**192- Sorting array elements. Arrays are ordered; only **object keys**193 are sorted.194- Truncating an `*Id` or `hash` field. Identifiers are load-bearing195 for fixture lookups even in tests.196- Treating a `.jsonl` file as a single JSON document. JSONL is197 newline-delimited; each line is a separate fingerprint.198- Inferring a schema from one file. Need ≥ 2 to detect drift.199- Trimming arrays outside `artifacts.*`. Top-level conversation200 arrays (`$.messages`, `$.thread.*`) are off-limits to `trim`.201 **`trim` only descends into `artifacts.*` subtrees.**202- Trimming a structural array (`panels`, `widgets`, `queries`).203 Those describe the dashboard's shape, not data points. The204 allowlist is strict by design — do not add a `default` fallback205 budget that would catch them.206- Modifying any string inside `trim`. The mode is array-cardinality207 only. `content`, panel descriptions, tool arguments — every208 string round-trips byte-identical. Use `shrink` if string209 truncation is wanted.210211---212213## Definition of Done214215- [ ] Corpus resolved to ≥ 2 files with a state line printed.216- [ ] Every file has a fingerprint.217- [ ] Drift report produced (clusters listed, outlier key paths218 enumerated, severity assigned).219- [ ] If `normalize` or `shrink`: every rewritten file re-parses and220 post-rewrite drift ≤ pre-rewrite drift.221- [ ] Final one-line summary: `N files / M clusters / K drift sites222 (severity HIGH/MED/LOW)`.223224---225226## Diagnosable227228This skill declares a diagnostic surface at229[`rules/diagnostic-surface.md`](./rules/diagnostic-surface.md) — phase230model, failure taxonomy (F-novel-seeded), existing-guards table, and231hard invariants. Run `/create-skill diagnose optimize-mock-data` after232a failed or unsatisfactory run to get a confidence-gated unified diff233that hardens the skill against the same failure class.