|
|
|
@@ -32,6 +32,57 @@ A recording can be long but still fast-tier (two known speakers, plain language)
|
|
|
|
|
|
|
|
|
|
**One metadata field is the exception by design: `asr_note`.** When you record a run's correction history in the transcript's frontmatter, use the key `asr_note` — Stage 1 masks its value before matching, so the verbatim old forms your ledger cites (`修正含:<old-form>→<canonical>`) never re-fire as phantom matches, and no phantom review items get enqueued from it. The field stays a full-fidelity ledger; the masking keeps line numbers exact and splices the ledger back untouched. Before this existed, one such ledger line produced 18 phantom Stage 1 matches and 9 phantom queue enqueues on every re-run — a per-file tax that recurred forever. **Two constraints, both load-bearing: the value must sit on the same line as the key (single-line flow style — a multi-line `asr_note: |` / folded value is out of the masker's reach and stays unprotected), and other frontmatter keys are processed normally — keep correction ledgers under `asr_note` and nowhere else.**
|
|
|
|
|
|
|
|
|
|
### Evidence selection and escalation
|
|
|
|
|
|
|
|
|
|
Use this branch when a term remains uncertain, a review queue is being resumed,
|
|
|
|
|
or a requested media channel is unavailable. Choose evidence that can distinguish
|
|
|
|
|
the competing readings; do not make completing a fixed retrieval sequence the goal.
|
|
|
|
|
|
|
|
|
|
1. **Consume existing decisions first.** A user's explicit correction settles that
|
|
|
|
|
occurrence immediately. Resolve its queue row and verify the file edit; if the
|
|
|
|
|
anchor moved, use `--reanchor-review` and retry the same verdict. Do not ask for
|
|
|
|
|
audio, repeat the question, or rerun ASR to validate what the user just confirmed.
|
|
|
|
|
Reusability is a separate decision under the dictionary/identity matrix.
|
|
|
|
|
2. **Use the raw conversation as evidence.** Read the complete turn, its responses,
|
|
|
|
|
and relevant later mentions. A plausible ASR sound change plus an explicit
|
|
|
|
|
same-referent definition, correction, spelling, or unambiguous raw occurrence
|
|
|
|
|
can settle ordinary wording or a technical term without audio. Record that
|
|
|
|
|
basis as text-supported, not audio-verified. Mere fluency, occurrence counts,
|
|
|
|
|
another model's confidence, or a proposed rewrite making more sense are not
|
|
|
|
|
independent evidence. Never use an earlier AI edit as its own corroboration.
|
|
|
|
|
3. **Check another available source when it would change the verdict.** Reuse an
|
|
|
|
|
existing parallel transcript, the meeting's written material, a relevant roster,
|
|
|
|
|
or an exact project/tool identifier. A written source can settle spelling or
|
|
|
|
|
referent; it does not prove how a word was pronounced. Preserve the person-name
|
|
|
|
|
and speaker-identity gates. Do not turn a speaker's factual error or self-correction
|
|
|
|
|
into correct technical prose, or change a number, unit, or negation just because
|
|
|
|
|
the alternative matches domain knowledge.
|
|
|
|
|
4. **Use audio when accessible and useful.** Download through an authorized channel
|
|
|
|
|
when available. If export is denied, stop that export attempt and use other
|
|
|
|
|
evidence; do not make granting download permission the user's next required
|
|
|
|
|
task. Already-authorized playback can help only if the runtime can actually hear
|
|
|
|
|
or transcribe the relevant utterance. Seeing a player, captions, or a screenshot
|
|
|
|
|
is not hearing audio. Do not bypass export controls, guess another account, or
|
|
|
|
|
claim an unavailable listening capability. The clip/recognizer contract in
|
|
|
|
|
`advanced_correction_evidence.md` still applies when running that route.
|
|
|
|
|
5. **Adjudicate the item, then continue the others.** Apply supported corrections
|
|
|
|
|
through their queue rows. Use `kept_original` only when evidence supports the
|
|
|
|
|
original reading. Reject a speculative correction without manufacturing proof
|
|
|
|
|
that the original was spoken; if its underlying ambiguity remains, keep it
|
|
|
|
|
pending with the best candidate and the missing discriminator. A low-impact
|
|
|
|
|
unresolved fragment may remain in the draft without interrupting the user.
|
|
|
|
|
|
|
|
|
|
Ask the user only for a discriminator they possess: an unresolved identity, private
|
|
|
|
|
reference, or material wording/number/polarity whose competing readings still matter
|
|
|
|
|
after the available checks. Show the original span, candidate, evidence, and exact
|
|
|
|
|
missing fact. Do not send the queue wholesale as a checklist of mandatory approvals,
|
|
|
|
|
or ask again for a verdict already recorded. State what is resolved and what remains
|
|
|
|
|
unresolved; neither permission failure nor queue cleanup establishes transcript quality.
|
|
|
|
|
`skipped` is not evidence of a correct reading and must not manufacture a final/clean
|
|
|
|
|
claim while substantive uncertainty remains.
|
|
|
|
|
|
|
|
|
|
### Correction sequence
|
|
|
|
|
|
|
|
|
|
1. Run Stage 1 (dictionary) on all files (parallel if multiple)
|
|
|
|
|
2. Verify Stage 1 — diff against the original. If the dictionary introduced false positives, work from the **original** file instead and apply your edits there. **A false positive here is debt you owe the dictionary**: the same bad rule fires on every future transcript until retired, so the moment you spot one — a rule that turned correct speech wrong, especially "real-word → real-word" rules (both sides are valid-word-shaped, so the non-word guard doesn't catch them; and under `--apply-domain` every matching rule applies regardless of its risk class) — e.g. a `买买→卖卖` rule rewrote a correct "买买工作流" into "卖卖工作流" — disable it in the same session with `--report-false-positive <from_text> <to_text> -d <domain>` — pass the rule's stored from→to pair exactly as Stage 1's `*_changes.md` shows it (the From/To columns) or as it sits in the dictionary, NOT "wrong-word → right-word" semantics. The direction is counter-intuitive for a false positive: the `买买→卖卖` rule stored `from=买买, to=卖卖` (it rewrote a correct 买买 into a wrong 卖卖), so you pass `"买买" "卖卖"` — the rule's stored from→to pair, which is what the tool keys on. One call disables the rule and lowers its confidence (the tool prints "The rule has been disabled"); it will not fire on the next transcript. If the word is genuinely *ambiguous* (correct in some contexts, wrong only here) rather than plain wrong, don't disable the rule — record the disambiguating cue in the domain context file instead. Fixing this transcript while leaving the trap armed guarantees the next one trips it too.
|
|
|
|
|
**And when the input already passed through an automated corrector** (a sync pipeline's pre-classify stage, a previous Stage 3 API run), your input is NOT raw ASR — upstream corrections are baked in with no evidence trail. Before triaging, diff against the raw source (the caller's raw transcript — sync engines typically keep one alongside the corrected copy, e.g. `transcript_raw.txt` — or re-pull from the source API). Two things fall out of that diff, in opposite directions: **(a)** every upstream entity swap is itself a suspect in step 4's triage *until its provenance is checked* (below), because an upstream AI "correction" can be a fluent wrong guess — real case: raw ASR 「新的车辆」 was "smoothed" by a pipeline AI into 「新出来的反馈」 (grammatical, plausible, wrong: the speaker said a near-homophone name), and only the raw diff caught it; **(b)** what upstream already fixed correctly is settled — check the diff *before* proposing a fix that's already applied, or you redo work and risk "fixing" a correct form back to a wrong one
|
|
|
|
@@ -114,7 +165,7 @@ A recording can be long but still fast-tier (two known speakers, plain language)
|
|
|
|
|
6. **WebSearch** — only for genuinely public entities (a public-company ticker, a known researcher, a drug name). Skip for anything project-internal.
|
|
|
|
|
7. **The audio itself — one clip through a second recognizer.** Every rung above searches *records*; this one returns to the signal. Use it only for a **load-bearing** token that remains unresolved, when source audio is available and the current authorization already permits a second local or external recognizer. Otherwise enqueue or ask once. Cut tight and medium clips, verify the returned text contains the token's neighbouring words, and use an engine from a genuinely **different family** than the transcript producer. Cross-family agreement is strong counter-evidence against an unsupported sound-distant rewrite, not proof: it corroborates the *sound* and never chooses between homophonic spellings or overrides the person-name gate. For an alphabetic token, search exact local authorities first; use WebSearch only after evidence makes the entity public or the user authorizes sending the token externally. Method, timestamp trap, two-re-cut ceiling, result table, and cost boundary: [advanced_correction_evidence.md](advanced_correction_evidence.md) § *One recording, two engines*.
|
|
|
|
|
|
|
|
|
|
Only after all of these strike out do you ask the user — and by then you've shown the entity isn't already recorded on this machine, which makes the ask legitimate. A confirmed result becomes a Confident fix; if the search *can't* confirm it, it drops to Uncertain. **Batch these**: collect the unique unknowns and run the ladder once per unique entity, not once per occurrence. **When asking for a canonical person name, always preserve an escape hatch outside your shortlist** — include an `Other / none of these` path (or verify the UI supplies one) that accepts the user's exact free-text spelling. A single local occurrence can justify putting a candidate on the list, but not making the list exhaustive: the real canonical may be an English name while every candidate you found is a Chinese transliteration.
|
|
|
|
|
Stop the search when available evidence settles the occurrence. If it does not, apply [evidence selection and escalation](#evidence-selection-and-escalation): an unavailable rung does not make the user the automatic next tool, and a question is justified by the missing discriminator and its consequence, not by having exhausted a checklist. A confirmed result becomes a Confident fix; if the search *can't* confirm it, it drops to Uncertain. **Batch these**: collect the unique unknowns and run the ladder once per unique entity, not once per occurrence. **When asking for a canonical person name, always preserve an escape hatch outside your shortlist** — include an `Other / none of these` path (or verify the UI supplies one) that accepts the user's exact free-text spelling. A single local occurrence can justify putting a candidate on the list, but not making the list exhaustive: the real canonical may be an English name while every candidate you found is a Chinese transliteration.
|
|
|
|
|
|
|
|
|
|
**And when the user answers, their verdict is ✅ authoritative — the strongest source in this whole loop — and it compounds three ways in the same session.** A user who says "X is actually Y (my colleague on team Z)" has handed you a source stronger than any local document. Cash it in immediately: ① apply the fix; ② persist the variant where it compounds — an important recurring person goes to the **people roster** (per [dictionary_identity_and_context.md](dictionary_identity_and_context.md)), a project term or one-off name goes to `--add ... --domain <project>` (the same ASR will mishear the same name again next week); ③ record it in the ledger / roster / domain context with the user's words, the date, and a ✅ "user-confirmed" marker — no later session should re-ask. Two refinements learned the hard way:
|
|
|
|
|
- **Collision-check the FROM side before dict-adding.** If the garbled string is itself a real person's name elsewhere in your world (another project's roster holds a *different* real `李明`), a `李明`→`黎明` dictionary rule will corrupt that person's future transcripts. That fix belongs in the domain context file as a trap with its disambiguating cue ("in editing-team context, `李明` = `黎明`"), never in the dictionary.
|
|
|
|
@@ -153,7 +204,7 @@ A recording can be long but still fast-tier (two known speakers, plain language)
|
|
|
|
|
- **Prefer the smallest edit that explains the error — rank candidates by phonetic distance before you judge any of them.** The rule above bounds how *much* one candidate may change; this one decides *which* candidate wins when several would read fine. ASR errors are small perturbations — the engine maps a heard sound to the nearest word it knows — so among candidates that all make sense, the one changing the fewest phonemes is almost always what was said. Useful fingerprint in Mandarin: **a reduplicated or multi-syllable tail surviving intact while only the leading syllable differs** points at an initial-consonant confusion (retroflex/alveolar `sh`/`s`, `zh`/`z`, `ch`/`c`, and the `n`/`l`, `f`/`h` pairs), so search same-final/different-initial candidates *before* concluding the whole word was misheard.
|
|
|
|
|
**Where this fails is not while you generate candidates — it's while you audit text that is already there** (an upstream correction, or a fix you accepted on the first pass). Reviewing existing text puts you in verify-mode: you ask "is this reasonable?", it is, and you move on — never noticing you were handed one candidate rather than a ranked set. A candidate that rewrites three syllables can be perfectly idiomatic *and* be a rewrite; the only thing that separates it from the one-phoneme candidate is that you generated both and compared. So when auditing any already-applied correction, force the question: **is there a smaller edit that also explains this?** If you cannot answer it, you have validated rather than verified. **One priority outranks phonetic minimality: the in-document self-proof — the intended word occurs correctly elsewhere in the passage, the passage is *about* that referent, and the proof occurrences check out against the raw text** (occurrences written by the same upstream pass prove nothing — judge and judged sharing a source is circular). If both candidates occur correctly, the proof is non-discriminating; fall back to phonetic minimality or the queue. And when the fix under audit is rule-backed, a raw-verified self-proof against it *is* the affirmative evidence step 2's revert bar asks for — revert, then retire or scope the rule there. (Real case 2026-08: an already-applied fix read 「完全」 where the raw was 「原全」 — nearest sound, perfectly fluent — but the surrounding exchange said 「全职」 three times in the raw and was *about* 全职; the phonetically smaller candidate was the wrong one, and only the self-proof check caught it.)
|
|
|
|
|
A second-pass subagent that returns 8 sharp rows beats one that returns 8000 tokens of narration every time. Task works when you're in the main context; if it truly isn't available — e.g. these instructions are themselves running inside a subagent, which can't spawn another — do one more thorough line-by-line re-read yourself from the corrected artifact. Do not substitute a known-pattern grep and call it a re-read. Never skip the second pass over a missing tool.
|
|
|
|
|
7. **Emit a needs-checking list AND enqueue it** — the chat summary alone evaporates when the session ends, so every *Uncertain* item gets dual-written: (a) in your chat summary to the human — line number, the original text you left in place, what you suspect, why you couldn't confirm it; (b) into the persistent review queue via `--enqueue-review items.json` (see [review_queue_dashboard.md](review_queue_dashboard.md); item field/alias schema: [script_parameters.md](script_parameters.md) §Review Queue Item Schema — unknown keys are silently dropped, so write `line`, not `line_hint`) with the same fields plus a proposed action pack, so the human can one-keystroke-resolve it later in the dashboard — or a later agent session can close it with new evidence (`--resolve-review ID --decision … --note "<evidence>"`). Entity/name questions get `kind: entity` because a wrong identity has higher business impact and therefore leads the queue; the human verdict still fixes only this occurrence unless the separate reuse matrix admits a dictionary/roster/context update. Pure phrasing doubts get `kind: wording`. If nothing is uncertain, say so. A minimal `items.json` for `--enqueue-review` (one object per uncertain item; `suggested` may be empty when you have no candidate — the dashboard lets a human fill it later):
|
|
|
|
|
7. **Record unresolved items AND enqueue them** — apply [evidence selection and escalation](#evidence-selection-and-escalation) before treating any item as a human question. The chat summary alone evaporates when the session ends, so every *Uncertain* item gets dual-written: (a) in your chat summary to the human — line number, the original text you left in place, what you suspect, why you couldn't confirm it; (b) into the persistent review queue via `--enqueue-review items.json` (see [review_queue_dashboard.md](review_queue_dashboard.md); item field/alias schema: [script_parameters.md](script_parameters.md) §Review Queue Item Schema — unknown keys are silently dropped, so write `line`, not `line_hint`) with the same fields plus a proposed action pack, so the human can one-keystroke-resolve it later in the dashboard — or a later agent session can close it with new evidence (`--resolve-review ID --decision … --note "<evidence>"`). Entity/name questions get `kind: entity` because a wrong identity has higher business impact and therefore leads the queue; the human verdict still fixes only this occurrence unless the separate reuse matrix admits a dictionary/roster/context update. Pure phrasing doubts get `kind: wording`. If nothing is uncertain, say so. A minimal `items.json` for `--enqueue-review` (one object per uncertain item; `suggested` may be empty when you have no candidate — the dashboard lets a human fill it later):
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
[
|
|
|
|
|