twocents/open source

Transcription

Generate transcripts with your own program and retry failed jobs.

GUILDSCRIBE_TRANSCRIPTION_HOOK selects one executable worker. Omitting the setting disables transcription. Guildscribe does not choose or bundle a model adapter. Guildscribe saves transcription progress so unfinished jobs can resume after a restart. The short lifecycle-hook timeout does not apply to transcription.

After saving a completed recording, Guildscribe inserts a transcripts row with status pending. It processes one transcription at a time, separately from Discord capture, so recording can continue while transcription runs. The states are pending, running, complete, and failed. The validated state retains attempts, queuedAt, nullable startedAt, nullable finishedAt, a sanitized nullable error, and a nullable result. Daemon startup requeues pending work and converts an interrupted running state back to pending. It does not automatically retry failed work or retroactively enqueue recordings that predate configuration of the hook.

Guildscribe invokes the executable with exactly one positional argument:

/absolute/path/to/transcription-hook /absolute/path/to/recording

The recording directory is also the child working directory. The executable receives no request document on stdin. It discovers inputs in the recording directory: participants.json, audio.mp3, and the JSON/PCM pairs below intervals/. Recording metadata is available through read-only SQL using GUILDSCRIBE_DATABASE and GUILDSCRIBE_RECORDING_ID in the hook environment. These replace the former recording.json input; existing adapters must be updated before enabling the upgraded daemon.

The restricted child environment contains ordinary process-location values and the two database/recording references above; it never contains the Discord token. Before exiting successfully, the executable atomically writes a result document like this to .transcript-result.json.pending:

The text and interval ID here are illustrative. Use an interval ID from the recording, or omit it for combined-audio transcription.

{
  "language": "en",
  "segments": [
    {
      "startOffsetMs": 0,
      "endOffsetMs": 1200,
      "text": "Readable segment text.",
      "sourceIntervalID": "00000000-0000-4000-8000-000000000000"
    }
  ]
}

language is a nullable BCP 47 tag. sourceIntervalID is optional for combined-audio transcription; when present, it must identify overlapping speaking audio in the same recording. Speaker identity is resolved through the source interval and participants.json; Guildscribe snapshots the resolved speaker into segment columns for SQL-only searches. Segments must contain nonempty text, start at or after zero, end after they start, be ordered by startOffsetMs, and remain within the recording duration. Exit code zero is accepted only when the pending result exists and passes schema validation.

The pending document is the hook's response; stdout and stderr are bounded diagnostics only. The hook must not write the database. After it exits zero, Guildscribe validates the result, snapshots speakers, and atomically commits segments and status complete. The validated in-memory representation is:

{
  "schemaVersion": 1,
  "recordingID": "00000000-0000-4000-8000-000000000000",
  "status": "complete",
  "attempts": 1,
  "queuedAt": "2026-11-05T12:30:00.000Z",
  "startedAt": "2026-11-05T12:30:01.000Z",
  "finishedAt": "2026-11-05T12:31:00.000Z",
  "error": null,
  "result": {
    "language": "en",
    "segments": []
  }
}

result is null before completion and on failure. Failed work requires both finishedAt and error; complete work requires finishedAt and result. The hook must remain running until its atomic write is complete; Guildscribe does not poll an invalid file indefinitely. A nonzero exit, spawn failure, missing result, or invalid result saves status failed. Graceful daemon shutdown returns an in-flight job to pending. Audio, interval sidecars, and PCM are retained for every outcome. The worker never creates FLAC, WAV, or consolidated per-speaker tracks.

After the result is saved, Guildscribe emits the best-effort transcription.completed or transcription.failed lifecycle notification when configured. Hook stdout and stderr are bounded diagnostics and are not result transport.

Retry failed work

Replace the example UUID with an ID from guildscribe recordings list:

{ "recordingID": "00000000-0000-4000-8000-000000000000" }
guildscribe transcript retry --request retry.json --dry-run
guildscribe transcript retry --request retry.json
guildscribe transcript get RECORDING_ID --include-segments

Retry queues work for the existing daemon; it does not start a daemon or another worker. Failed work becomes pending, pending/running requests are no-ops, and complete work is rejected. See history for exports.

Supported integrations

The current runtime uses the pending-result file described above. Asynchronous transcript segment add, transcript complete, and transcript fail callbacks are planned, not implemented commands. Use the generated catalog to check your installed version.

Your hook selects the model or service and manages its scheduling and credentials. Guildscribe provides recording IDs and speaker information, validates the transcript, and writes it to the database. Capture continues when transcription is absent or fails. No summarization or reasoning stage follows transcription.

On this page