Skip to main content

transcript-fixer

Corrects ASR/STT transcription errors — homophones, garbled terms, person-name errors, mixed Chinese/English — with dictionary rules plus Claude's built-in AI, no external API key required. Use to clean meeting notes, lecture transcripts, interviews or speech-recognition text, even when the user only says "fix this transcript". Not for structuring a clean transcript into minutes (use meeting-minutes-taker).

Source facts

Repository
daymade/claude-code-skills
Last source activity
October 13, 2026 at 19:16
Detected SKILL.md language
English
Stars
1,466
Forks
226

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
100 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
transcript-fixer
description
Corrects ASR/STT transcription errors — homophones, garbled terms, person-name errors, mixed Chinese/English — with dictionary rules plus Claude's built-in AI, no external API key required. Use to clean meeting notes, lecture transcripts, interviews or speech-recognition text, even when the user only says "fix this transcript". Not for structuring a clean transcript into minutes (use meeting-minutes-taker).
# Transcript Fixer Use a two-phase loop: 1. Stage 1 applies deterministic, already-known corrections. 2. Native AI Correction reads the complete transcript, fixes one-off errors, verifies uncertain entities, and compounds reusable fixes. **Native AI Correction is the default. Stage 1 alone is incomplete.** Stage 3 API exists only for automation that has no Claude/Codex agent available. ## Operating contract - Finish Stage 1 → Native AI Correction → compound confirmed recurring fixes. Do not report a transcript clean after Stage 1 alone. - Skip Native AI only when the human explicitly limits this run to the dictionary pass or a dated artifact proves Native AI already ran on this exact transcript. - In Claude Code or Codex, do not run Stage 3. Use Stage 1 plus the native workflow. - Never rewrite speech for fluency. A correction must explain a plausible ASR error and preserve who said what. - A standard term elsewhere or a known participant is a candidate source, not proof of this occurrence's wording. Apply the occurrence-specific contrasts in [Native review packets](references/native_review_packets.md#occurrence-specific-evidence) during the first pass and cold review; retain the raw form when the discriminator is missing. - By default, never infer or reassign speaker identities. Preserve speaker-label lines; human-confirmed labels and user verdicts are authoritative. Only an explicit, task-scoped user grant to infer identities opens the separate derived-reader branch: read [scoped speaker inference](references/scoped_speaker_inference.md) before acting. That grant is permission to infer, not an identity confirmation or permission for other transcripts. - When comparing two transcripts of the same session (a 1.3x-sped Feishu 妙记 vs a full-speed one, DJI vs 录音豆, two devices, or any duplicate/overlapping capture), do NOT let an alignment script's output stand in for reading. Script diffs (`difflib`, normalize-and-compare, per-turn speaker-label counts) are locators for *candidate* divergent segments only — their similarity ratios, "identical turn counts", and "speaker-divergence counts" are never the conclusion. Read both transcripts turn by turn yourself and decide from that. Real case 2026-09-18: a per-line regex that missed continuation lines reported "150 speaker divergences, all empty" then flipped to "1" once multi-line bodies were parsed — the whole numeric range was parser noise, and a canonical swap was nearly made on it. Corroboration that is genuinely independent requires a different recognizer over the source audio (rung 7), not a re-segmentation of the same recognition. - Parsing a Feishu 妙记 `.txt` (or any `speaker HH:MM:SS.mmm` transcript): the spoken text lives on the lines AFTER the speaker-label line, up to the next label — not on the label line itself. A single-line regex (`group(3)` of the label) reads every multi-line turn's body as empty. Counting speakers or turns: use UTF-8-safe tooling (Python), never `sort | uniq -c` or `awk '{print $1}'` — both silently collapse distinct CJK speaker labels into one (2026-08-29 `sort|uniq` folded 5 speakers' 106 turns into "说话人A 106"; 2026-09-18 `awk $1` reported "说话人A 373" for a file whose real distribution was 说话人A17/说话人B287/说话人C60/说话人D9). - Before correcting any person name, directly read both the configured global people roster and the owning project's explicit identity roster or alias ledger. Stage 1 auto-loads only global `ASR 变体` entries; it does not load project rosters or expose suppressed, disabled, and unlisted entries. If an expected source is missing or the sources conflict, leave the name unchanged and enqueue or ask once. Never use occurrence frequency as identity evidence. Read [references/dictionary_identity_and_context.md](references/dictionary_identity_and_context.md) before settling the name. - Resolve doubts from available evidence before escalating. Audio download is one evidence channel, not a prerequisite for Native correction. When it is unavailable, follow [evidence selection and escalation](references/native_ai_full_workflow.md#evidence-selection-and-escalation); do not require the user to change download permissions or treat every pending row as a question only they can answer. - Leave genuinely unresolved text unchanged and enqueue it. A visible garble is safer than a fluent wrong guess; a pending row records uncertainty, not an automatic human handoff. - Treat an unfamiliar token as unknown, not as an error. Exhaust the local evidence ladder first. For a load-bearing token that remains unresolved, use the clip-level cross-recognizer rung only when source audio and a permitted second engine are already available; otherwise continue with the available evidence under the escalation policy above. Agreement from a genuinely different recognizer family strongly corroborates the sound, but never chooses between homophonic spellings or overrides the person-name gate. Read native workflow step 4, rung 7 before using it. **A backlog of exhausted pendings is the batch form of this rung**: when a native pass leaves a queue of locally-unresolvable rows, do not hand the queue to the user wholesale — check it with `verify_queue_audio.py` (one transcript, one source audio, one second engine, two clip windows per row). The script can attach acoustic evidence but never records a verdict; identical clips and readings that do not support the disputed word boundary remain pending for adjudication. The operator matrix and timestamp-mapping pitfalls live in [advanced_correction_evidence.md](references/advanced_correction_evidence.md) § Batch pending adjudication. - Treat a single-line `asr_note` value as correction provenance: it intentionally cites old forms and is excluded from matching. Multi-line YAML ledger values are not masked; keywords, titles, other ASR-derived metadata, and body text remain in correction scope. - Read [references/native_ai_full_workflow.md](references/native_ai_full_workflow.md) in full before performing a native pass. Contiguous, nonoverlapping reads may cover the full file; if a response is truncated, read the missing range before proceeding. An executed `cat` does not establish that its full output was received. Read the task-specific references named below before their corresponding action. ## Run context Run every entrypoint through `uv run`; entrypoints that need third-party Python packages declare them with PEP 723, while stdlib/internal-only utilities may omit the metadata block. Execute commands from the skill directory printed when this skill was invoked, or prefix every script path with that directory. Do not rely on `$CLAUDE_SKILL_DIR`; it is not available in every harness. If the bundle location is genuinely unknown, use the installation-resolution procedure in [references/installation_setup.md](references/installation_setup.md). Do not select the first result from a broad `find`: caches, backups, and old versions can coexist. ## Quick start ~~~bash # Initialize once uv run scripts/fix_transcription.py --init # Stage 1 for one project domain. --apply-domain trusts that explicitly # selected, human-curated project domain at every risk level. uv run scripts/fix_transcription.py \ --input meeting.md --stage 1 \ --domain myproject --apply-domain --json # Several sibling domains may be loaded as one union. uv run scripts/fix_transcription.py \ --input meeting.md --stage 1 \ --domain myproject,myproject-alt --apply-domain --json # Preview without writing the Stage 1 output. uv run scripts/fix_transcription.py \ --input meeting.md --stage 1 --domain myproject --dry-run # Scan all documented context traps after the native read-through. uv run scripts/fix_transcription.py --scan-traps \ --context-file ~/.transcript-fixer/contexts/myproject.md \ --input meeting.md ~~~ Safe mode is the Stage 1 default: low-risk rules apply; medium/high-risk matches defer to `*_needs_review.md` and the persistent review queue. `Applied: 0` is a valid result, not proof that the transcript is clean. The Stage 1 JSON contract is: ~~~json { "applied": 0, "deferred": 0, "output_path": null, "needs_review_path": null, "input_unchanged": true, "review_enqueued": 0, "stage1_only_incomplete": true, "stage2_total_chunks": 0, "stage2_failed_chunks": 0, "stage2_degraded": false, "boundary_refused": 0, "boundary_check_active": true } ~~~ Read every field in the result. `boundary_refused` counts dictionary matches the word-boundary check refused this run — neither applied nor deferred, so a caller comparing runs can see why a deferral disappeared; `--apply-all` switches that check off. `stage1_only_incomplete` is additive to the original caller contract and must remain true for a Stage 1 script run; only the caller can close it by running Native AI, or by explicitly choosing the agent-less Stage 2/3 route. The `stage2_*` telemetry fields are always present: Stage 1 reports `0`, `0`, and `false`; Stage 2/3 replace them with the actual API outcome. Do not infer no-op or success from whether a sidecar exists. For a native end-to-end example, read [references/example_session_dji_minutes.md](references/example_session_dji_minutes.md). ## Choose the route | Route | Use when | Required reading | |---|---|---| | Pending continuation | Dated Native/review evidence covers this exact file; only uncertain rows remain, or the user supplies a verdict | [pending-continuation.md](references/pending-continuation.md); reuse unchanged completed coverage | | Fast native | Short/plain transcript, known speakers, low stakes | This file + [native_ai_full_workflow.md](references/native_ai_full_workflow.md) | | Full native | Domain-heavy, unfamiliar entities, 3+ speakers, long or decision-bearing transcript | [native_ai_full_workflow.md](references/native_ai_full_workflow.md), plus queue and evidence references below | | Scoped speaker inference | User explicitly authorizes identity inference for this transcript/task | [scoped_speaker_inference.md](references/scoped_speaker_inference.md); keep ordinary ASR correction on the native route | | Caller integration | Another skill or ingest pipeline invokes Stage 1 | `Cross-skill caller contract` below | | Review queue/dashboard | Any item is uncertain or needs audio | [review_queue_dashboard.md](references/review_queue_dashboard.md) | | Agent-less API | CI/batch automation with no agent available | [glm_api_setup.md](references/glm_api_setup.md) and [workflow_guide.md](references/workflow_guide.md) | | Multi-file batch | Several related transcripts; especially 10+ files | [advanced_correction_evidence.md](references/advanced_correction_evidence.md) | Use vocabulary and stakes as the primary tier signals; use length only as a tiebreaker. A five-minute medical interview can require the full tier, while a long plain two-person memo can use the fast tier. ## Native correction checklist 1. **Give the file its final name before Stage 1.** Queue anchors store absolute paths. Use a human-readable project filename before any deferral can enqueue. When the input arrives as inline text with no file yet — a slash-command argument, a pasted block — write it to a file before anything else; `--input` and the queue anchors both need a path, and a scratch location is fine when nothing downstream will archive it. No `--domain` given and none obvious from context? Omitting the flag already defaults to searching every domain (`--domain`'s own default), so don't block on picking one — run Stage 1 bare and let safe mode gate what auto-applies. If a specific candidate still needs resolving, one step of the ladder is cheap enough to keep even at fast tier though the rest of it isn't: native_ai_full_workflow.md step 4's rung 1, a single cross-domain lookup — `--lookup "<term>"` prints every existing claim on the term (dictionary rules active or disabled, as FROM or TO; context rules; roster variants; queue rows) — not the full verification ladder the tier table tells you to skip, just that one query. 2. **Recover the raw baseline before reading a pre-corrected transcript.** If an ingest pipeline or previous API pass already touched the text, diff against the raw source first. Judge upstream edits as edits, not as ground truth. 3. **Load project priors and read the complete transcript.** Read `~/.transcript-fixer/contexts/<domain>.md` when present, then read the whole file before deciding early ambiguities. 4. **Run Stage 1 and inspect the real result.** Prefer explicit project domains plus `--apply-domain --json`. Read `deferred` and `review_enqueued`; never silently discard the sidecar or queue gap. 5. **Diff Stage 1 against raw/original.** If a rule changed correct speech, work from the original, retire the stored pair with `--report-false-positive "<from>" "<to>" --domain <domain>`, and verify it no longer fires. 6. **Triage every candidate.** - Confident: the sound change is plausible and context or an authoritative local source settles it. - Needs verification: a person, company, product, model, ticker, place, number, or other load-bearing term without a source. - Uncertain: evidence does not settle it; leave the original and enqueue. - Multi-channel entity fork: when independent transcripts disagree on a person name or other proper noun and no local authority settles it, collect the unresolved forks and ask the human once. Do not guess, and do not treat a majority vote as identity evidence. 7. **Apply the smallest edit that explains the sound.** Do not add words the speaker did not say. Correct ASR-derived metadata too, while leaving `asr_note` intact. A Stage 1 deferral you now agree with is closed through its queue row, not re-applied with sed: `--resolve-review <id> --decision accepted` (ids from `--list-review --review-file "<absolute-canonical-file>" --json`) performs the edit, or records a fix you already applied by hand without writing. Either way the row ends `accepted`; `kept_original` asserts the transcript was right as spoken and is never the exit for a fix you applied. 8. **Run a second pass.** - Every tier: run `--scan-traps` and inspect both hits and `unparsed`. - Full tier: use fresh-context review. For a single unsplit review, assign one corrected file and require a compact residual table or explicit `no new residuals`; an empty/truncated response is a failed review. - For a split, multi-file, or resumed Full review, follow [native_review_packets.md](references/native_review_packets.md) for packet assignment, JSON results, validation, and recovery. - High-stakes multi-recording: a sampled clip settles only that anchored item. If the user asked for a higher-quality or complete transcript and the baseline audio is available, load **`/daymade-audio:asr-transcribe-to-text`** and run its full-file transcription path across the complete clearest/canonical recording before claiming whole-transcript coverage; otherwise report `sampled cross-check only — incomplete`. Prefer a recognizer different from the producer of the canonical body. If only the same recognizer is available, the run proves complete-source coverage but is not independent cross-recognizer corroboration; state that boundary. 9. **Enqueue every unresolved item; escalate selectively.** First apply [evidence selection and escalation](references/native_ai_full_workflow.md#evidence-selection-and-escalation), then follow `Review queue safety` below and [review_queue_dashboard.md](references/review_queue_dashboard.md). Open only this file when human review is needed. Detection and enqueueing are not correction: for a higher-quality/final claim, every queue row anchored to this exact file must leave `pending`. For human review, start the dashboard with `uv run scripts/review-dashboard/server.py --file "<absolute-canonical-file>"`; add `--item <id>` to land on one fork. If a human is unavailable, keep the artifact explicitly labeled `draft / unresolved — incomplete` and enumerate the rows; do not ship the raw suspect text under a completed quality claim. 10. **Read back the human state, then finalize.** When the human says they marked the dashboard, do not rerun ASR or ask the same questions again. First run `uv run scripts/fix_transcription.py --list-review --review-file "<absolute-canonical-file>" --review-status all --json`, apply any resulting file state, and require `stats.pending_total == 0` for that exact path; zero pending rows is required before the high-quality/final claim. Then diff the file actually edited, run numeric consistency when numbers matter, rerun plain Stage 1, re-grep known corrections, and confirm every change traces to a triage decision. Global queue counts cannot close or reopen this file's quality claim. Last, run `--close-sidecars --input "<absolute-canonical-file>"`: it re-reads every `*_changes.md`/`*_needs_review.md` entry against the file and the queue, refuses while an entry still reads as the original without a verdict or any row is pending, and removes the sidecars only when everything is closed (see `Finalization`). **Authority for a person-name verdict travels in `--authority` or a cited `--authority-record`, never in `--note`.** `--note` is your reason. The guard previews the cited authority; evidence is appended only with the successful verdict transaction, so a refused correction leaves the queue unchanged. Sources include roster rows, group displayName+nickName double-read, an explicit user decision, or audio evidence. A person-name `accepted`/`overridden` written without it is refused; a `kept_original` resting on such a source leaves the guard nothing to read and leaves `evidence` still asserting the enqueue-time doubt. Real case 2026-09-22: two queue rows for one nickname were settled with the citation in `--note`, so `--show-review` read `evidence` = "待用户确认" beside `decision_note` = resolved — one item carrying both "unresolved" and "settled" state. Repair with `--attach-authority ID --authority-text TEXT`, which appends and never rewrites. The two commands cannot share one line: `--attach-authority` and `--resolve-review` together are refused, because the natural one-liner "verified it, now settle it" ran only the append, printed ✅, exited 0 — and never recorded the verdict while telling the caller it had. For an explicit answer, use `--authority-record <JSON>` binding `item_id`, `target`, `source_ref` and exact `quote`; a bare yes also cites the original `question`. Inspect the actual user message first: the checker validates the citation's shape and scope, not its truth. A naked “用户明确回答” label, pending, denied, missing or empty evidence never substitutes for that source. Use the reference's finite affirmative-answer shapes; mentioning a target does not confirm it. Unsupported wording stays pending rather than being rewritten into a supported answer. When updating an existing single-line `asr_note` correction ledger, add `--ledger-entry` to the same verdict call: the resolved text and ledger entry share one file replacement. It does not create or rewrite the whole note. Read [cited answers and ledger writes](references/review_queue_dashboard.md#cited-answers-and-ledger-writes) for the schema and interrupted file/SQLite recovery. Inspect exit status and JSON/readback before downstream work; separate inspected calls, `&&` or checked subprocess execution stop on failure, while newline chaining can hide it behind a later successful command. 11. **Compound the learning in the same turn.** Route each stable pattern to its correct home; do not leave confirmed fixes only in chat. Native-pass edits never reach Stage 1's correction history, so harvest them mechanically right after the final diff: ~~~bash # Diff raw vs corrected into parseable trap candidates (review artifact — # you adjudicate the printed list; --write auto-appends only the recurring # (≥2x) non-bare candidates; --write-all also appends the one-off set) uv run scripts/harvest_corrections.py raw.md corrected.md \ --context-file ~/.transcript-fixer/contexts/<domain>.md ~~~ Every emitted bullet is round-trip verified through the real trap parser before printing, and pairs already documented in the context file are skipped. A pair the bullet grammar cannot carry — a side with no lexical content or a `*` in it, such as a vendor's `***` redaction mask diffed against the real word — is dropped at the noise filter, and any bullet that still fails to parse is reported on stderr and excluded rather than aborting the run. High-frequency candidates are strong traps; single-occurrence ones need a human judgment — that is why `--write` leaves them out by default — and ⚠️ 裸形 candidates are never auto-written. This replaces hand-writing trap bullets from memory. 12. **Propagate entity fixes deliberately.** First finish the same-file sweep: inspect `harvest_corrections.py --json` entries with nonzero `remaining`, then check the observed spelling family of each confirmed entity or technical identifier across body and ASR-derived metadata. Use the authoritative spelling and conventional filename casing; a user's informal dictation is not a request to preserve a typo. Preserve real alternate referents, aliases, and generic-character hits. Do not turn a same-file sweep into an unreviewed batch replacement or repeat settled questions. Then search only the owning project’s derived notes/summaries and review every hit; exclude raw ASR and correction sidecars because they preserve the evidence trail. The detailed provenance bar, local-first entity ladder, second-pass prompt, queue payload, and finalization rules are in [references/native_ai_full_workflow.md](references/native_ai_full_workflow.md). ## Cross-skill caller contract A caller pipeline must: 1. Run Stage 1 with the explicitly configured project domain(s), `--apply-domain`, and `--json`. If `deferred > review_enqueued`, persist the review sidecar outside any temporary directory or surface the gap as failure. 2. Run Native AI with this skill loaded, or report `Stage 1 only — incomplete`. Agent-less automation may use Stage 3 instead. Canonical call: ~~~bash uv run scripts/fix_transcription.py \ --input "$staged" --stage 1 \ --domain "$domains" --apply-domain --json ~~~ A caller that wires only the script path never loads this contract. Script-path integration alone is therefore a Stage 1 prefilter, not transcript correction. Keep project domains warm: every confirmed recurring correction from the native pass must be added back to the correct project domain, roster, or context file. ## Dictionary and identity safety Read [references/false_positive_guide.md](references/false_positive_guide.md) and [references/dictionary_identity_and_context.md](references/dictionary_identity_and_context.md) before adding a rule. | Pattern | Destination | |---|---| | Stable non-word or unique garble → canonical term | `--add ... --domain <project>` | | Important recurring person and observed ASR variants | People roster | | Correction right only inside a specific recurring phrase | `--add-context-rule PATTERN REPLACEMENT --domain <project>` (regex, domain-scoped; omit `--domain` for global) | | Common/real word wrong only under a cue | Domain context trap, never a bare rule (a bare number or a single surname + 老师/总 is refused at roster load and by `--add` / `--import`, `--force` included) | | Real name → different real name | Domain context + human/audio verification, never a bare rule | | Confirmed-correct entity repeatedly reopened | Confirmed-correct context record | | One-off sentence-local wording | Edit only; do not add |
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub