Transcript Weaver
Transcript Weaver is a Python 3.10+ Unix-style pipeline for safely acquiring, transforming, and filing transcripts:
Installation
Install Transcript Weaver with all currently advertised functionality, including Otter support:
python3 -m pip install transcript-weaver
For local development from a repository checkout:
python3 -m pip install --editable .
Otter acquisition currently supports WSL on Windows and controls the user's existing
Windows Chrome through Chrome DevTools integration. Browser automation is intentional:
Otter currently reserves its supported public API for Enterprise workspaces. The
Playwright Python dependency is installed with Transcript Weaver, but Weaver does not
require Playwright to download or maintain a separate bundled browser. Windows Chrome,
WSL-to-Windows connectivity, a dedicated Chrome profile, and one-time port-proxy setup
are still required; see
docs/otter.md
for the rationale and setup
instructions before attempting trwinp otter.
trwinp -> normalized JSON -> trweave -> enriched JSON -> trwout
trwinp otter | trweave franks-example | trwout franks-example
Standard output is reserved for JSON packets. Diagnostics and errors use standard
error. Ordinary successful operation is silent except that trwinp and trweave
emit their packet. trwout writes a file and emits no normal output.
Packet contract
trwinp emits schema version 1, the creating project version, and a canonical UTC recording time:
{
"schema_version": 1,
"trw_version": "1.0.0001",
"run": {"id": "20260805-120000-a1b2"},
"datetime": "2026-08-05T08:30:00Z",
"source": {"type": "otter", "name": "Morning note", "reference": "https://otter.ai/u/example"},
"transcript": "Speaker 1 10:00 : Um, I am grateful this moring Transcribe by https://otter.ai",
"metadata": {"duration_seconds": 180.0}
}
trweave deeply verifies that all those fields remain unchanged and permits the
provider to add fields. A valid result includes:
{
"schema_version": 1,
"trw_version": "1.0.0001",
"run": {"id": "20260805-120000-a1b2"},
"datetime": "2026-08-05T08:30:00Z",
"source": {"type": "otter", "name": "Morning note", "reference": "https://otter.ai/u/example"},
"transcript": "Speaker 1 10:00 : Um, I am grateful this moring Transcribe by https://otter.ai",
"metadata": {"duration_seconds": 180.0},
"weave": {"type": "gratitude", "update_transcript": "- I am grateful for this morning."}
}
The same valid run.id survives every stage. A missing ID is generated by any stage,
and a malformed ID is rejected. The trw_version field is informational bug-report
metadata, not a compatibility gate: downstream stages preserve it when present but
accept packets created by any version, including legacy packets without the field.
When Otter exposes a reliable media duration, trwinp records it as
metadata.duration_seconds. Franks Example deterministically routes values greater than
300 seconds to unknown; exactly 300 seconds remains content-classified.
Configuration
On first use, the packaged schema-v1 configuration and prototype-derived
prompts/example.md are copied atomically to the platform's per-user configuration
directory. Existing files are never merged, migrated, repaired, or overwritten. Tests
always inject disposable paths.
Older configuration files are not automatically migrated. If you want to replace an early prototype configuration with the complete example, first back it up and then run a command so Transcript Weaver can create the new defaults:
mv ~/.config/transcript-weaver/config.json ~/.config/transcript-weaver/config.json.backup
printf 'configuration check' | trwinp stdin > /tmp/transcript-weaver-packet.json
Alternatively, add the fields named by the validation error to your existing file. The program will never overwrite your real-vault settings.
A coherent configuration looks like this:
{
"schema_version": 1,
"logging": {"retained_runs": 5},
"providers": {
"gemini": {
"model": "gemini-3.5-flash-lite",
"credential": {"source": "pass", "name": "api/gemini"}
}
},
"weave": {
"franks-example": {
"provider": "gemini",
"prompt_file": "prompts/example.md"
}
},
"out": {
"franks-example": {
"timezone": "America/Los_Angeles",
"vault": {
"path": "transcript-weaver-test-output",
"relative_to": "cwd"
},
"packet_fields": {
"category": "weave.type",
"content": "weave.update_transcript"
},
"destination_roots": {
"journals": "SubDir1/SubDir2"
},
"destinations": {
"gratitude": {
"operation": "insert",
"root": "journals",
"file": "Gratitude Journal.md",
"format": "## {date}\n\n{content}\n\n"
},
"dream": {
"operation": "insert",
"root": "journals",
"file": "Dream Journal.md",
"format": "## {date}\n\n{content}\n\n"
},
"ses": {
"operation": "insert",
"root": "journals",
"file": "SEs Journal.md",
"format": "## {date}\n\n{content}\n\n"
},
"sacred": {
"operation": "insert",
"root": "journals",
"file": "Sacred Journey.md",
"format": "## {date}\n\n{content}\n\n"
},
"unknown": {
"operation": "create",
"directory": "00 Inbox",
"filename": "unknown-{date}-{time}.md",
"format": "## {date}\n\n{content}\n\n"
}
}
}
}
}
Provider, weave, output, and destination names are matched case-insensitively. Names differing only by case are rejected.
Path objects use path plus relative_to: "cwd" or relative_to: "config" for
relative paths. For an absolute path such as /mnt/d/Notes or a home-relative path
such as ~/Obsidian/Notes, omit relative_to. Existing path strings remain supported
for compatibility. Keys beginning with _comment are embedded documentation and are
ignored; other unexpected keys are rejected.
Each output profile’s vault is the common root for all its destinations.
An optional destination_roots object defines reusable relative paths beneath the
resolved vault. A destination selects a root with its explicit root field; root
names are matched case-insensitively, and names differing only by case are rejected.
Unknown root names produce an error listing the available roots. Placeholder forms
such as {journals} are not supported.
Resolution with a selected root is:
vault + destination root + destination file/directory
When root is omitted, the backward-compatible resolution is:
vault + destination file/directory
Destination roots, files, and directories must be relative. They cannot escape the
resolved vault with .. or by resolving through a link to a location outside it.
In the example, the four journals are under vault/SubDir1/SubDir2; the unrooted unknown
destination remains under the vault-level 00 Inbox.
Weaving
Need an LLM API but do not have one yet? See Free and Low-Cost LLM APIs for general information about free tiers, low-cost gateways, and important trade-offs. Transcript Weaver does not currently support every provider discussed there.
trweave PROMPT_OR_PROFILE first treats its argument as a path, resolving a
relative path from the current working directory. A readable UTF-8 regular file
supplies the complete prompt and uses the configured Gemini provider. Otherwise,
the argument is resolved as a weave profile. No extension guessing or file
searching occurs.
Gemini credentials are read from pass; secrets never belong in configuration.
The implementation uses Gemini's REST API directly, requests JSON, and retries
transient network errors and HTTP 429/5xx responses up to four times, waiting 1,
4, 9, and 16 seconds. Every retry is reported on standard error and is also
written to the persistent run log when --log is enabled. Every input field
is immutable. If the provider replaces transcript, Weaver preserves the original
and uses that attempted replacement only when needed to populate
weave.update_transcript; no duplicate top-level transformed-text field is emitted. Other deleted or modified input fields remain errors. Fenced, explanatory,
partial, non-object, or invalid weave responses are rejected.
Output
trwout OUTPUT_PROFILE extracts category and content through the configured
dotted packet fields. It converts the packet's UTC datetime to the profile's IANA
timezone without modifying the packet. {date}, {time}, and {content} are the
only formatting placeholders.
insertatomically places date-only headings in ascending local-date order.- Multiple entries on the same local date are retained in arrival order, separated by
---, and a warning identifying the date is written to standard error. appendatomically appends the configured rendered text.createcreates a standalone file atomically and refuses to overwrite one.
The example stores Dream Journal.md, Gratitude Journal.md, SEs Journal.md,
and Sacred Journey.md beneath the configured vault-relative journal root. Unknown
material is routed to the vault-relative 00 Inbox directory as
unknown-{date}-{time}.md. The packaged prompt asks for approximately 72-character
word-boundary wrapping while keeping long-form unknown content substantially verbatim.
Logging and privacy
All commands accept --log, --verbose, and --debug-artifacts. Logs are
optional, grouped by run ID, and retained according to logging.retained_runs.
They may contain stage, profile, model, operation, relative destination, timing,
and sanitized failure information. Ordinary logs never contain transcripts, prompts,
transformed content, provider bodies, credentials, or complete journals.
If a provider changes or deletes an immutable packet field, trweave writes the
complete original and provider JSON documents under
<log-directory>/packet-failures/. These files are intentionally sufficient for a
manual diff and therefore may contain complete transcripts and other private packet
data. Inspect them before sharing. They are grouped by run ID and bounded by
logging.retained_runs; the current failure is always retained. Debug artifacts are
also potentially sensitive.
Project version and distribution builds
src/transcript_weaver/_version.py is the single source of truth for the project
version used by package metadata, transcript_weaver.__version__, and the packet
field trw_version.
The public format is major.minor.build, with a four-digit build component. Python packaging canonicalizes numeric release components, so application version 1.0.0001 appears in wheel/sdist filenames and Core Metadata as the PEP 440-equivalent 1.0.1; the packaged _version.py and emitted packets retain 1.0.0001.
Create distributions with:
python -m transcript_weaver.build
That intentional command takes an exclusive build lock, increments the build component once, atomically updates the source version, and builds both distributions. It restores the previous source version if the build fails. Imports, tests, installs, and ordinary packaging metadata inspection do not increment the version. A stale lock must be investigated and removed manually after confirming no build is active.
Development and tests
pytest -m 'not live_otter and not live_gemini'
ruff check .
ruff format --check .
mypy
python -m transcript_weaver.build
The default suite is offline and uses fake providers and miniature temporary vaults. Live Otter and Gemini checks are opt-in. After all offline checks pass:
TRANSCRIPT_WEAVER_LIVE_GEMINI=1 pytest --no-cov -m live_gemini tests/test_live_gemini.py
The live Otter functional test uses the configured WSL/Windows Chrome session:
TRANSCRIPT_WEAVER_LIVE_OTTER=1 pytest --no-cov -m live_otter tests/test_live_otter.py
That command reads pass api/gemini and sends only the harmless test transcript
to Gemini. It does not access an Obsidian vault or Otter.
License
Copyright © 2026 Frank Merrow.
Transcript Weaver is free software licensed under the GNU General Public License, version 3 or later. See the LICENSE file for the complete terms.
Metadata
Release files for transcript-weaver 1.0.11
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| transcript_weaver-1.0.11.tar.gz | 68.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| transcript_weaver-1.0.11-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 124.6 kB
Release files / transcript_weaver-1.0.11.tar.gz
| Download URL | transcript_weaver-1.0.11.tar.gz |
|---|---|
| Size | 68.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
30b63c6ad47d581eb88cc1f5b7490136a1ba5e97dfd9f8da6e30315209969e43
|
|
BLAKE2b-256 checksum How to use checksums |
b2e662db3adb65959d4af9ef9e357ca575b958ef359d57c452bdfce67f01227d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.12
|
Release files / transcript_weaver-1.0.11-py3-none-any.whl
| Download URL | transcript_weaver-1.0.11-py3-none-any.whl |
|---|---|
| Size | 56.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a6701cc45d0cead2ebbdfaa1eafe173d098ebdf080f6d4a2f5adb20dc477a426
|
|
BLAKE2b-256 checksum How to use checksums |
27a0e8629a7b9e2338a59dd07e0665ad23ea98b48a1dc656f07a1d2125d2b0f3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.12
|