/wispr-mining
Mine Wispr SQLite for ASR vocabulary gaps/corrections. Triggers: wispr mining, ASR errors, voice vocabulary.
$ golems-cli skills install wispr-miningUpdated 2 weeks ago
Extract ASR misrecognition patterns from Wispr Flow's SQLite database and generate clean, importable dictionary files.
Database Location
~/Library/Application Support/Wispr Flow/flow.sqlite
CRITICAL: Always work on a COPY. Before any operation:
cp ~/Library/Application\ Support/Wispr\ Flow/flow.sqlite /tmp/wispr-flow-readonly.sqliteThen query /tmp/wispr-flow-readonly.sqlite exclusively. Never write to the production database.
Schema Reference
Dictionary Table
| Column | Type | Purpose |
|---|---|---|
id | VARCHAR(36) | UUID primary key |
phrase | VARCHAR(255) | The dictionary entry (vocabulary word or trigger phrase) |
replacement | VARCHAR(255) | NULL = vocabulary entry. Non-NULL = replacement mapping |
frequencyUsed | INTEGER | How often this entry has fired |
isDeleted | TINYINT(1) | Soft delete flag |
isSnippet | TINYINT(1) | Snippet (long-form expansion), not a correction |
manualEntry | TINYINT(1) | User-added vs auto-detected |
createdAt | DATETIME | When added |
lastUsed | DATETIME | Last trigger time |
History Table
| Column | Type | Purpose |
|---|---|---|
transcriptEntityId | VARCHAR(36) | UUID primary key |
asrText | TEXT | Raw ASR output (what the mic heard) |
formattedText | TEXT | After Wispr's formatter + dictionary |
editedText | TEXT | User's manual correction (NULL if no edit) |
app | VARCHAR(255) | Which app was active |
timestamp | DATETIME | When dictated |
numWords | INTEGER | Word count |
isArchived | TINYINT(1) | Archive flag |
Mining Workflow
Step 1: Copy Database
cp ~/Library/Application\ Support/Wispr\ Flow/flow.sqlite /tmp/wispr-flow-readonly.sqliteStep 2: Current Dictionary Audit
-- Active dictionary entries (not deleted, not snippets)
SELECT phrase, replacement, frequencyUsed, lastUsed
FROM Dictionary
WHERE isDeleted = 0 AND isSnippet = 0
ORDER BY frequencyUsed DESC;
-- Unused entries (candidates for cleanup)
SELECT phrase, replacement, createdAt
FROM Dictionary
WHERE isDeleted = 0 AND isSnippet = 0 AND frequencyUsed = 0
ORDER BY createdAt;
-- Snippets (long-form expansions)
SELECT phrase, replacement FROM Dictionary
WHERE isDeleted = 0 AND isSnippet = 1;Step 3: Find ASR Misrecognition Patterns
-- Words that ASR consistently gets wrong (asrText differs from formattedText)
-- Group by the ASR mistake to find systematic patterns
SELECT
LOWER(asrText) as asr_pattern,
formattedText as corrected_to,
COUNT(*) as occurrences,
GROUP_CONCAT(DISTINCT app) as apps
FROM History
WHERE asrText IS NOT NULL
AND formattedText IS NOT NULL
AND asrText != formattedText
AND isArchived = 0
AND LENGTH(asrText) < 100
GROUP BY LOWER(asrText)
HAVING COUNT(*) >= 3
ORDER BY occurrences DESC
LIMIT 50;Step 4: Find User Edit Patterns
-- Cases where user manually corrected the formatted text
-- These are the HIGHEST-SIGNAL gaps
SELECT
formattedText,
editedText,
app,
timestamp
FROM History
WHERE editedText IS NOT NULL
AND editedText != formattedText
AND isArchived = 0
AND LENGTH(editedText) < 200
ORDER BY timestamp DESC
LIMIT 50;IMPORTANT: Classify edits into two buckets:
- Whitespace-only edits (trailing spaces, leading newlines, paragraph breaks) — LOW signal, ignore for dictionary purposes
- Real content corrections (word changes, missing words, ASR errors) — HIGH signal, these drive new entries
Use TRIM(formattedText) != TRIM(editedText) to filter out whitespace-only edits. Report both counts but only act on content corrections.
Step 5: Cross-Reference with Existing Dictionary
For each ASR pattern found in Step 3, check if it's already covered:
-- Check if a misrecognition is already in dictionary
SELECT phrase, replacement
FROM Dictionary
WHERE isDeleted = 0
AND (phrase LIKE '%<pattern>%' OR replacement LIKE '%<pattern>%');Only recommend NEW entries that aren't already covered.
Step 6: Generate Output Files
OUTPUT FORMAT IS CRITICAL. Wispr Flow imports CSV files literally — every line becomes a dictionary entry.
Vocabulary File (new words to recognize)
File: wispr-vocabulary-update.csv
Format: One word per line. NO headers. NO comments. NO blank lines. NO quotes unless the word itself contains a comma.
toml
TOML
golems.toml
agentic
Replacements File (trigger → correction mappings)
File: wispr-replacements.csv
Format: trigger,replacement per line. NO headers. NO comments. NO blank lines. NO quotes unless values contain commas.
tomo,toml
golems.tomo,golems.toml
Claw.md,CLAUDE.md
Full SKILL.md source — includes LLM directives, anti-patterns, and technical instructions stripped from the Overview tab.
Extract ASR misrecognition patterns from Wispr Flow's SQLite database and generate clean, importable dictionary files.
Database Location
~/Library/Application Support/Wispr Flow/flow.sqlite
CRITICAL: Always work on a COPY. Before any operation:
cp ~/Library/Application\ Support/Wispr\ Flow/flow.sqlite /tmp/wispr-flow-readonly.sqliteThen query /tmp/wispr-flow-readonly.sqlite exclusively. Never write to the production database.
Schema Reference
Dictionary Table
| Column | Type | Purpose |
|---|---|---|
id | VARCHAR(36) | UUID primary key |
phrase | VARCHAR(255) | The dictionary entry (vocabulary word or trigger phrase) |
replacement | VARCHAR(255) | NULL = vocabulary entry. Non-NULL = replacement mapping |
frequencyUsed | INTEGER | How often this entry has fired |
isDeleted | TINYINT(1) | Soft delete flag |
isSnippet | TINYINT(1) | Snippet (long-form expansion), not a correction |
manualEntry | TINYINT(1) | User-added vs auto-detected |
createdAt | DATETIME | When added |
lastUsed | DATETIME | Last trigger time |
History Table
| Column | Type | Purpose |
|---|---|---|
transcriptEntityId | VARCHAR(36) | UUID primary key |
asrText | TEXT | Raw ASR output (what the mic heard) |
formattedText | TEXT | After Wispr's formatter + dictionary |
editedText | TEXT | User's manual correction (NULL if no edit) |
app | VARCHAR(255) | Which app was active |
timestamp | DATETIME | When dictated |
numWords | INTEGER | Word count |
isArchived | TINYINT(1) | Archive flag |
Mining Workflow
Step 1: Copy Database
cp ~/Library/Application\ Support/Wispr\ Flow/flow.sqlite /tmp/wispr-flow-readonly.sqliteStep 2: Current Dictionary Audit
-- Active dictionary entries (not deleted, not snippets)
SELECT phrase, replacement, frequencyUsed, lastUsed
FROM Dictionary
WHERE isDeleted = 0 AND isSnippet = 0
ORDER BY frequencyUsed DESC;
-- Unused entries (candidates for cleanup)
SELECT phrase, replacement, createdAt
FROM Dictionary
WHERE isDeleted = 0 AND isSnippet = 0 AND frequencyUsed = 0
ORDER BY createdAt;
-- Snippets (long-form expansions)
SELECT phrase, replacement FROM Dictionary
WHERE isDeleted = 0 AND isSnippet = 1;Step 3: Find ASR Misrecognition Patterns
-- Words that ASR consistently gets wrong (asrText differs from formattedText)
-- Group by the ASR mistake to find systematic patterns
SELECT
LOWER(asrText) as asr_pattern,
formattedText as corrected_to,
COUNT(*) as occurrences,
GROUP_CONCAT(DISTINCT app) as apps
FROM History
WHERE asrText IS NOT NULL
AND formattedText IS NOT NULL
AND asrText != formattedText
AND isArchived = 0
AND LENGTH(asrText) < 100
GROUP BY LOWER(asrText)
HAVING COUNT(*) >= 3
ORDER BY occurrences DESC
LIMIT 50;Step 4: Find User Edit Patterns
-- Cases where user manually corrected the formatted text
-- These are the HIGHEST-SIGNAL gaps
SELECT
formattedText,
editedText,
app,
timestamp
FROM History
WHERE editedText IS NOT NULL
AND editedText != formattedText
AND isArchived = 0
AND LENGTH(editedText) < 200
ORDER BY timestamp DESC
LIMIT 50;IMPORTANT: Classify edits into two buckets:
- Whitespace-only edits (trailing spaces, leading newlines, paragraph breaks) — LOW signal, ignore for dictionary purposes
- Real content corrections (word changes, missing words, ASR errors) — HIGH signal, these drive new entries
Use TRIM(formattedText) != TRIM(editedText) to filter out whitespace-only edits. Report both counts but only act on content corrections.
Step 5: Cross-Reference with Existing Dictionary
For each ASR pattern found in Step 3, check if it's already covered:
-- Check if a misrecognition is already in dictionary
SELECT phrase, replacement
FROM Dictionary
WHERE isDeleted = 0
AND (phrase LIKE '%<pattern>%' OR replacement LIKE '%<pattern>%');Only recommend NEW entries that aren't already covered.
Step 6: Generate Output Files
OUTPUT FORMAT IS CRITICAL. Wispr Flow imports CSV files literally — every line becomes a dictionary entry.
Vocabulary File (new words to recognize)
File: wispr-vocabulary-update.csv
Format: One word per line. NO headers. NO comments. NO blank lines. NO quotes unless the word itself contains a comma.
toml
TOML
golems.toml
agentic
Replacements File (trigger → correction mappings)
File: wispr-replacements.csv
Format: trigger,replacement per line. NO headers. NO comments. NO blank lines. NO quotes unless values contain commas.
tomo,toml
golems.tomo,golems.toml
Claw.md,CLAUDE.md
Output Validation Gate
MANDATORY before declaring done. Run these checks on every generated file:
# Check 1: No comment lines (lines starting with #)
grep -c '^#' OUTPUT_FILE && echo "FAIL: Comments found" || echo "PASS: No comments"
# Check 2: No blank lines
grep -c '^$' OUTPUT_FILE && echo "FAIL: Blank lines found" || echo "PASS: No blank lines"
# Check 3: No header lines (common CSV headers)
grep -ciE '^(phrase|word|trigger|replacement|vocabulary|category)' OUTPUT_FILE && echo "FAIL: Header found" || echo "PASS: No headers"
# Check 4: Replacements file has exactly one comma per line
awk -F',' 'NF!=2 {print "FAIL line " NR ": " $0; exit 1}' REPLACEMENTS_FILE && echo "PASS: Format correct" || true
# Check 5: No markdown formatting
grep -cE '^\||\*\*|^##|^-' OUTPUT_FILE && echo "FAIL: Markdown found" || echo "PASS: No markdown"If ANY check fails, fix the file and re-validate. Do NOT declare success with failing validation.
Anti-Patterns
| DO NOT | WHY | DO INSTEAD |
|---|---|---|
Add # comment lines to CSV | Wispr imports them as dictionary entries | Keep files pure data only |
Add CSV headers (phrase,replacement) | Imported as a dictionary entry | Start with data directly |
| Put stats/analysis in the CSV | Gets imported as entries | Write analysis to a separate .md file |
| Add blank lines for readability | May create empty dictionary entries | No blank lines ever |
| Combine vocabulary + replacements | Different import workflows | Two separate files always |
| Query the production database | Risk of corruption | Copy to /tmp first |
| Add entries without checking existing | Creates duplicates | Always cross-reference Step 5 |
Output Deliverables
Every run MUST produce exactly 3 files:
wispr-vocabulary-update.csv— New vocabulary words (one per line, no metadata)wispr-replacements.csv— New trigger→replacement mappings (trigger,replacement per line)wispr-mining-report.md— Analysis report with:- Total records analyzed
- Top ASR misrecognition patterns (with counts)
- User edit patterns found
- Dictionary effectiveness stats (% of entries with >0 uses)
- Recommended entries with justification
- Gap analysis (high-frequency misrecognitions not in dictionary)
The .md report is where analysis goes. The CSVs are PURE DATA ONLY.
Periodic Mining Schedule
Run this skill quarterly or when:
- User reports frequent voice corrections in a new domain
- New project vocabulary emerges (new repo names, tool names, client names)
- Dictionary cleanup needed (removing unused entries)
Best Pass Rate
91%
Opus 4.6
Assertions
23
3 models tested
Avg Cost / Run
$0.2193
across models
Fastest (p50)
3.4s
Haiku 4.5
Behavior Evals
Phase 2 baseline — skill quality on ClaudeBehavior Baseline
| Assertion | Opus 4.6 | Sonnet 4.6 | Haiku 4.5 | Consensus |
|---|---|---|---|---|
| copies-database-first | 3/3 | |||
| no-direct-production-query | 3/3 | |||
| no-hash-comments | 3/3 | |||
| no-csv-headers | 1/3 | |||
| no-blank-lines | 2/3 | |||
| no-markdown-in-csv | 2/3 | |||
| vocabulary-file-created | 2/3 | |||
| replacements-file-created | 2/3 | |||
| report-file-created | 3/3 | |||
| queries-dictionary-table | 3/3 | |||
| notes-existing-coverage | 2/3 | |||
| no-duplicate-recommendations | 3/3 | |||
| runs-grep-validation | 3/3 | |||
| reports-validation-results | 1/3 | |||
| no-success-without-validation | 3/3 | |||
| queries-edited-text | 2/3 | |||
| distinguishes-whitespace-vs-content | 3/3 | |||
| uses-frequency-data | 0/3 | |||
| analysis-in-md-file | 2/3 | |||
| no-analysis-in-csv | 2/3 | |||
| one-comma-per-line | 3/3 | |||
| no-multi-column-csv | 3/3 | |||
| no-quoted-fields-unless-needed | 2/3 |
Token Usage
Cost per Run
| Model | Input Tokens | Output Tokens | Cost / Run | Cost / 1K Runs |
|---|---|---|---|---|
| Opus 4.6 | 7,011 | 6,585 | $0.5990 | $599.00 |
| Sonnet 4.6 | 3,263 | 2,971 | $0.0544 | $54.40 |
| Haiku 4.5 | 2,573 | 3,179 | $0.0046 | $4.60 |
Response Time (p50)
Response Time (p95)
| Model | p50 | p95 | Overhead |
|---|---|---|---|
| Opus 4.6 | 8.3s | 11.9s | +44% |
| Sonnet 4.6 | 5.1s | 7.9s | +56% |
| Haiku 4.5 | 3.4s | 5.6s | +65% |
Last evaluated: 2026-03-12 · Data is generated from skill assertions (real cross-model benchmarks coming soon)
Changelog entries are derived from eval runs and skill version updates. Full cascading changelog (Phase 4D) coming soon.
Best Pass Rate
91%
Assertions
23
Models Tested
3
Evals Run
8
- +Initial release to Golems skill library
- +23 assertions across 8 eval scenarios