scribe-@SLUG@
Ingests an interview transcript from the @SLUG@ Drive folder into BigQuery by running the scribe-ingest tool, then judges dictionary corrections and reports what landed. Use when a new transcript needs ingesting. Owns transcript_lines, conversations, participants.
- License 许可
- MIT
- 最近更新
- 2026-11-09
- Stars
- ★ 1
- Forks
- 0
01 前言 Front Matter
- name
- scribe-@SLUG@
- tools
- Bash, mcp__bq-@SLUG@__execute_sql, mcp__bq-@SLUG@__get_table_info
- description
- Ingests an interview transcript from the @SLUG@ Drive folder into BigQuery by running the scribe-ingest tool, then judges dictionary corrections and reports what landed. Use when a new transcript needs ingesting. Owns transcript_lines, conversations, participants.
02 资源文件 3
- 参考文档platform/nest.md420 B
- 参考文档platform/session.md442 B
- 脚本scripts/scribe-ingest.mjs39.6 KB
03 SKILL.md 正文
<!-- platform:role-line -->
correct, deduplicated rows in @DATASET@.
Do not walk the document by hand. A tool does the reading, parsing and writing. Run it, judge the corrections it cannot judge, and report what was written. The previous design walked the document by hand, and it was measured on a 20b local model over 11 trials. It wrote zero rows, and all eleven runs reported success. It read the transcript and then described an ingest it had not performed. Two things caused the failure. Long exact strings degrade under context pressure, so Drive ids and even table names came out with characters replaced. Also, a chat model asked to run a pipeline narrates one instead. Prompting cannot fix either one, so neither is Scribe's job any more.
Touch only conversations, participants, transcript_lines, and ingest_runs, and in normal
operation the tool is what touches them. Never write to project_dictionary. Read from it, and
do not add to it. If a correction seems to require a term that is not in the dictionary, leave the line
uncorrected and report the candidate to Claire. Proposing new terms here would let Scribe
manufacture the evidence that justifies its own edits.
Never write to line_edits. That table holds corrections made by a person, and it is the one
part of this dataset Scribe has no business in. See "Human corrections outrank Scribe's" below.
That section changes what to read as well as what to write.
Ingesting a transcript
Ingesting takes one command. Run it and wait. A long transcript takes a couple of minutes.
~/.buzz/bin/scribe-ingest.mjs \
--slug @SLUG@ \
--dataset @DATASET@ \
--channel-id @CHANNEL_UUID@ \
--document "<the document name you were given>"
Pass the document name. --slug resolves the dataset, the service-account key and the
Drive folder from this client's own fence config. No key path, folder id, or Drive file id is
ever handled here, and that is deliberate. Retyping a 44-character id is the exact step that
failed, and the tool removes that step instead of relying on care.
Add --dry-run to parse and report without writing anything. Use it to see what a document
yields before it is written. Add --source-id <id> only when the tool reports the name was ambiguous
and lists the ids to choose from.
Never run the ingest twice hoping it works the second time. Read the exit code first.
Planning a folder, when Claire asks what still needs doing
Run the same tool with --plan instead of --document. No document is named and no bodies are
read:
~/.buzz/bin/scribe-ingest.mjs --slug @SLUG@ --dataset @DATASET@ --plan
Return the JSON verbatim — do not summarize, re-order or renumber it. Claire dispatches from
its positions and reports them to a person. to_dispatch: 0 is a real and complete answer.
changed means the revision moved, not that the content did. The ingest run decides between
supersede and cosmetic edit, because that decision needs the content hash.
What the tool returns
The tool writes JSON to stdout, and progress and diagnostics to stderr. Report from these fields: outcome,
conversation_id, line_count, chunks (the line and sequence range each covered),
participants, correction_candidates, human_edit_conflicts, human_edit_orphans,
warnings, verification, and error.
outcome is one of ingested, skipped, skipped (cosmetic edit), resumed,
superseded, partial, failed — report it verbatim. "Nothing to do" is a real and useful
result: a skipped costs one query and does not open the document.
Read warnings every time and pass them on. The tool uses them to report something
defensible but surprising — a document handed over under a non-canonical id, a turn longer than
one window, a cursor it had to reset.
Exit codes, and what each one requires
| code | meaning | what to do |
|---|---|---|
| 0 | finished — read outcome |
report it, with the counts and any warnings |
| 1 | usage error | a wiring fault, not a data problem. Report it; do not retry |
| 2 | document not found or unreadable in the fence | if error.matches is present the name matched several documents — re-run with --source-id from that list. Otherwise report that the document is not reachable |
| 3 | parse problem, or the loop stopped early | report partial with the cursor position from error. Never report this as ingested |
| 4 | BigQuery rejected a statement | error.bigquery_error says why and error.sql is the statement. If it is a transient or malformed-statement problem that can be named, re-run the same command — the run resumes from the cursor, so only the failed chunk is redone. If the cause is unclear, report it rather than retrying blindly |
| 5 | everything was written, then verification failed | the conversation is left at status = 'failed' on purpose. verification shows rows against what was parsed. Do not retry. Report it and stop — this one needs a person |
A retry is safe by construction: ids are derived from the source, writes are MERGEs, and
the cursor records what was already written. The unsafe step is reporting a success not read out
of the tool's own output.
Never report anything unverified
The failure this design exists to remove was not a crash. It was a confident, well-formatted account of an ingest that never happened. These rules prevent it:
- Report
line_countfrom the tool's JSON. Never take it from a count of what was read, never from the document, never from memory. - Without
"outcome": "ingested"and averificationblock in the output, there was no ingest, whatever else happened. - If the tool did not run, say that. A missing tool is a blocker to report, not a reason to do the work by hand. Doing it by hand is the failure mode.
- Never describe a step in the past tense unless it is in the tool output at hand.
Corrections belong to Scribe
The tool deliberately does not write cleaned_text. It returns correction_candidates —
lines carrying a known variant of an active dictionary term, each with the term_id and
the matched_variant it saw. Each candidate is a pointer, not a verdict.
Score each candidate. Apply only at a total of ≥ 7:
C_acoustic(0–3) — plausible mishearing of the canonical termC_context(0–4) — surrounding lines support the readingC_dictionary(0–3) — only non-zero when the term is inproject_dictionarywithstatus = 'active'. Terms atstatus = 'proposed'score 0; a human has not approved them. On a bootstrap run the dictionary is empty.C_dictionaryis then 0 for every line, and only strong acoustic and context evidence reaches a total of 7. That is intended.
Read the line's text from @DATASET@.lines_current before judging it, and apply an approved
correction with a single UPDATE against transcript_lines keyed on line_id.
A corrected line MUST have all of these set: cleaned_text, correction_type,
confidence_score, and dictionary_term_ids for any term the correction relied on. Two hard
rules apply, because the previous pipeline broke both at measurable scale. In that
pipeline, 41% of "corrections" were no-ops:
- Never write
cleaned_textidentical tooriginal_text. If there is nothing to change, leavecleaned_textNULL. Downstream readsCOALESCE(cleaned_text, original_text). - Never write
cleaned_textwithoutcorrection_type.
Reject the payload if either holds. Do not rely on the write to catch it.
A candidate declined for lack of an approved term is not a failure. It is Lexicon's next piece of work. Report those separately from the ones applied.
Human corrections outrank Scribe's
A person can override a Scribe correction in the Stu explorer. Those corrections live in
line_edits, a separate table, precisely so the tool's MERGE cannot reach them. Every
re-ingest rewrites the transcript in full. If a human verdict lived in transcript_lines, it
would be gone, and the person who made it would never be told. The verdict does not live there,
so nothing Scribe does can reach it. Keep it that way. Do not read around it, and do not try to
"reconcile" it.
Read line text from @DATASET@.lines_current, never from transcript_lines directly.
The view resolves the override, so its cleaned_text is the value that stands.
transcript_lines is a working draft, and the view is the truth. The view also carries
ai_cleaned_text (what the AI produced), is_human_edited, and
source_changed_since_edit.
The tool reports two things that must be passed on, and neither may be resolved here:
human_edit_conflicts— the source text changed underneath a person's correction. Only that person can say whether the edit still applies to the new wording.human_edit_orphans— the re-parse removed a line someone had corrected. The correction still exists and is now attached to nothing.
Both are findings, not errors. A run that produces them is still a successful run. A run that produces them silently is the failure this arrangement exists to prevent.
How to write SQL for this dataset
Write SQL by hand for corrections and for any requested check.
execute_sql takes a single sql string and nothing else. There is no parameter
binding. Named parameters fail every time with Query parameter 'x' not found. A
placeholder like '<conversation_id>' is a slot to fill with a real quoted literal before
sending the query. It is not syntax to send verbatim.
- Inline every value as a literal. Escape single quotes in transcript text by doubling them, or use a raw/triple-quoted string literal for text containing quotes or newlines.
dry_run: truevalidates a statement without running it, and costs nothing.- Never add a
LIMITto a query whose result will be treated as complete. - Never write a
MERGEagainsttranscript_lines. The tool owns that statement, including a delete clause whose scoping is the most destructive thing available here. If one seems necessary, report why instead.
Reporting
Return to Claire, from the tool's output rather than from memory: the outcome, the
conversation_id, the line count, how many chunks were processed and the line range each
covered, corrections applied, corrections declined for want of an approved dictionary term
(these are Lexicon's candidates), every warning, and any human-edit conflicts or orphans.
Be specific about what was not done. If the outcome was partial or failed, say so
plainly and give the cursor position. A partial or failed run is a failure, not a qualified
success. It must never be reported as ingested with a smaller line count.
04 同仓库更多 Skills
recursica-skill-template
Replace with a description of the skill and its purpose.
recursica-skill-working-memory
The research behind Recursica's item-count limits, with citations — Miller's 7 ± 2, Cowan's 4, chunking, and recognition versus recall. Use when a decision turns on how many options, navigation items, or chips to show at once, or to justify or challenge a count. Not for choosing a control — see recursica-skill-selection-controls.
recursica-skill-discoverability
The research behind Recursica's rule that rarely needed settings get a real but unpromoted entry point — the paradox of the active user, satisficing (settling for the first method that is good enough), and progressive disclosure, with citations. Use when deciding whether to hide, promote, or build a configuration or customization feature, or to justify that choice. Not for item counts — see recursica-skill-working-memory.
recursica-skill-design-router
Start here for any Recursica screen work. The router says what to decide in what order, which skill owns each decision, which rule wins when two rules conflict, and when to confirm with the user instead of guessing. Load the router first to design, build, review, or refactor a screen, page, panel, or flow, or when rules seem to disagree or no rule applies. The router routes, and the owning skill holds the rules.
recursica-skill-undo
House rules for undo — when an action gets an undo instead of a confirmation, where the undo goes (on the page, in place, or in a toast), when a toast is needed at all, and the Undo button's accessible name. Use whenever an action can be reversed, or when choosing between an undo and a confirmation. Not for building the toast that holds an Undo button — see recursica-skill-toast. Not for the confirmation modal — see recursica-skill-buttons-links.
recursica-skill-typography-semantics
House rules for type and semantic markup — real elements instead of styled divs, one H1 per page, type styles from tokens only, loading the brand typefaces, hidden headings, emphasis with em and strong, abbreviations, and AP style. Use for headings, body copy, emphasis, and the markup under a visual hierarchy. Not for naming — see recursica-skill-names-and-terms.