Skip to content

fix(library): a voice note's header heads a turn by the label a header that starts a block gives - #609

Merged
jddunn merged 7 commits into
masterfrom
fix/otter-reader-follow-up
Oct 26, 2026
Merged

jddunn merged 7 commits into
masterfrom
fix/otter-reader-follow-up

Conversation

@jddunn

@jddunn jddunn commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

Summary

This follows up #553. Three kinds of voice note export still read wrong on master at e4aa628.

  1. A Word file whose only empty paragraph stands before the closing line. The paragraphs Unknown 0:00, Good morning., Unknown 0:09, Shall we start?, an empty paragraph and Transcribed by https://otter.ai read as one turn, Good morning. Unknown 0:09 Shall we start?. Before fix(library): an Otter export made without the speakers' names or without the times reads into its turns #553's last commit they read as two turns. The file's lines stand the same way as those of a one-turn export (Unknown 0:00, Good morning., We meet at 10:30, in the hall., a blank line, the closing line), so the blank lines cannot tell the two apart.
  2. A two-turn note whose first turn holds three lines that end in a time. Unknown 0:00\nGood morning.\nWe meet at 10:30\nThen leave at 11:30\nBack by 12:30\nSee you there.\n\nUnknown 0:20\nGood.\n\nTranscribed by https://otter.ai\n read as four turns, two of them said by We meet at and Back by. The reader counted such lines against the blank lines, and the count read the text as having no blocks. This is the failure from the review of fix(library): an Otter export made without the speakers' names or without the times reads into its turns #553 on its one-turn note, now in a text with a second turn.
  3. A Word file with no empty paragraphs. Unknown 0:00, Good morning., We meet at 10:30, Unknown 0:09, Shall we start? and the closing line read as a turn by We meet at at 630,000 ms, and fix(library): an Otter export made without the speakers' names or without the times reads into its turns #553 pinned that reading as a limit. The one-turn note Unknown 0:00, Good morning., We meet at 10:30, Then leave at 11:30, See you there. and the closing line read as two turns.

The fix. Otter gives every header of a voice note the same label. Each public export of a voice note found gives Unknown on every header, including a talk between two people (IceMan56/MET at 672354c9, Freda32/TalkMemo at d76a57c7). A voice note's header still heads a turn where a block starts. It now also heads one elsewhere when it gives the label of a header that starts a block, as long as words stand on the line straight after it. A line of words that ends in a time gives a label that no header gives, so it is words. The line straight after a header stays that header's words. A time alone gives no label, so a text of times alone is still read by its blank lines.

Input Master Now
(1) as a Word file, and as text one turn two turns by Unknown, at 0 and 9,000 ms
One-turn export of the same shape (We meet at 10:30, in the hall.), as text and as a Word file one turn one turn
(2) four turns two turns, the first holding all five lines of words
The one-turn note with a blank line before the closing line one turn one turn
(3) We meet at 10:30 straight before Unknown 0:09 turn 2 by We meet at at 630,000 ms two turns by Unknown, the first Good morning. We meet at 10:30
Mirror of (3): Unknown 10:30, The bus left at 9:09, Shall we start? header at 630,000 ms the same
(3) one-turn note with no blank line at all two turns one turn
Speaker 1 and Speaker 2 with Otter's blank lines, as text and as a Word file three turns three turns

The limit. The TSDoc and docs/features/LIBRARY_INDEX.md state it, and a test pins it. If a speaker is labelled apart and their header starts no block, nothing tells that header from a line of words. In a Word file with no empty paragraphs, Speaker 1 0:00, Good morning., Speaker 2 0:05, Hello. and the closing line now read as one turn by Speaker 1; master read two. Otter labels a speaker it tells apart but cannot name Speaker 1 or Speaker 2 (help centre), but no public export in a voice note's layout found gives any label but Unknown. With the blank lines Otter writes, each such header starts a block and heads its turn, and a test covers that. A time alone has the same limit because it gives no label: a Word file of 0:04, Hello., 0:09, Good., an empty paragraph and the closing line reads as one turn, as a one-turn export of those lines does.

Not changed here. Otter's own Word export does not write the layout of (1). It writes an empty paragraph after each turn's words except the last, and puts the closing line in the page footer. The export help article shows this in its example image, and so does a public export, the Brookings webcast transcript in JohnWBlack/fccps-ai-deliverables-governance at 810b9c92. mammoth 1.13.0 reads no footer (docx-reader.js). (1) is a Word file laid out from the text export.

Linked issues

Follows #553.

Type of change

  • Bug fix
  • Feature
  • Performance
  • Documentation
  • Refactor
  • Tests
  • Build or CI

How this was verified

I traced each input above through turnsOf by hand. src/cognition/library/__tests__/transcripts.test.ts pins every reading in the table and the limit. The test commit comes first and fails on master; the fix follows it. CI's Test & Lint job (type check, lint, tests) and the Browser tests job run them.

Migration notes

None.

Checklist

  • Tests cover the changed behavior.
  • Docs are updated.
  • The title follows Conventional Commits, with ! before the colon for a change that breaks users (feat!: or feat(api)!:).
  • No secrets or private data are included.
  • Every automated review thread is settled (how).
  • I have read this change and can explain every line.

Summary by Sourcery

Fix Otter voice-note turn detection across text and Word exports, especially when block separators are missing or transcript lines resemble headers.

Bug Fixes:

  • Correct Otter voice-note parsing when headers are missing separating blank lines, preserving headers based on their labels and treating time-ending transcript lines as words.
  • Ensure Word exports without empty paragraphs follow the same corrected voice-note turn detection as text exports.

Enhancements:

  • Document the updated Otter voice-note parsing rules and their ambiguity limits.

Documentation:

  • Update the library format documentation with the revised Otter voice-note header and block-detection behavior.

Tests:

  • Expand transcript coverage for multi-line time-ending turns, missing blank lines, Word exports, repeated and distinct speaker labels, and documented ambiguous cases.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Otter transcript parsing for voice-note labels and speaker headers, including exports with missing blank lines or consecutive headers.
    • Reduced cases where time-like text within a turn is mistaken for a new header, while recognizing valid time-only turn headers.
  • Documentation
    • Clarified how Otter text and Word exports are interpreted when headers, labels, and blank lines are ambiguous.

…starts a block gives

A Word file of a voice note whose only empty paragraph stands before the
closing line ("Unknown 0:00", "Good morning.", "Unknown 0:09", "Shall we
start?", an empty paragraph, the closing line) reads as one turn, the
second header among the first turn's words. Its lines stand as those of a
one-turn export whose turn holds a line that ends in a time ("We meet at
10:30", "in the hall."), which must read as one turn, so the blank lines
cannot tell the two apart. The label can: Otter gives every header of a
voice note the same label, and a line of words gives one no header gives.

Read by a count of such lines against the blank lines, a note of two
turns whose first turn holds three lines that end in a time heads turns
said by "We meet at" and "Back by". In a Word file with no empty
paragraphs, "We meet at 10:30" straight before "Unknown 0:09" heads a turn
said by "We meet at" at 10:30, and the one-turn note's later lines head
one too. The test that pinned that reading as a limit now pins the
reading by labels, and pins the limit that comes with it: a second
speaker's header that starts no block reads as words.
…r that starts a block gives

A voice note's header was told from a line of words that ends in a time
by the blank lines alone: by a count of such lines against the blank
lines, and, where no blank line parts two lines of words, by the one
before the closing line. A Word file whose only empty paragraph stands
before the closing line read its second header into the first turn's
words, since its lines stand as a one-turn export's do; a note of two
turns whose first turn holds three lines that end in a time read them as
headers; and in a Word file with no empty paragraphs a line of words
straight before the next header headed a turn.

Otter gives every header of a voice note the same label: each public
export of one found gives "Unknown" on every header, a talk between two
people's too. A voice note's header now heads a turn where a block
starts, as before, and elsewhere, with words on the line straight after
it, when it gives a label a header that starts a block gives. A line of
words that ends in a time gives a label no header gives and is words. A
speaker Otter labels apart ("Speaker 2") still heads a turn where a
block starts; in a Word file with no empty paragraphs such a header
starts no block and reads as words, and the TSDoc states that limit
with its input. A time alone gives no label and is read by its blank
lines as before.
…at starts a block gives

The library guide said a Word file whose only empty paragraph stands
before the closing line reads as one turn, and that in a Word file with
no empty paragraphs a line of words straight before the next header
heads a turn said by "We meet at". It now gives the rule by labels with
its inputs: both files read into their turns, a one-turn export of the
same shape reads as one, and a later line of a turn that ends in a time
is words with or without the blank lines. It states the limit that
comes with it, a second speaker's header that starts no block reading
as words in a Word file with no empty paragraphs, and that a text of
times alone, which gives no label, is still read by its blank lines.
@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 8f61239 and 497be43.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 10b4ea4 and ccc6dfd.

Files selected for processing: 4
  • docs/features/LIBRARY_INDEX.md
  • src/cognition/library/__tests__/transcripts.test.ts
  • src/cognition/library/transcripts/otter.ts
  • src/cognition/library/transcripts/word.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.

Walkthrough

Otter transcript parsing now distinguishes placement-based and label-based loose headers. It uses block starts and labels found at block starts to classify turns. Tests and documentation cover time-like lines, adjacent headers, voice-note labels, and Word exports without blank paragraphs.

Changes: Otter header recognition

Layer / File(s) Summary
Loose-header layout modes
src/cognition/library/transcripts/otter.ts
Layout definitions now distinguish placement-based headers, label-based headers, and layouts without loose headers. Helpers identify block starts and collect labels from headers at block starts.
Turn recognition and transcript cases
src/cognition/library/transcripts/otter.ts, src/cognition/library/__tests__/transcripts.test.ts, src/cognition/library/transcripts/word.ts, docs/features/LIBRARY_INDEX.md
Turn recognition applies the layout rules and rejects headers immediately after another header. Tests and documentation cover time-like lines, repeated or unrecognized labels, and Word exports without blank paragraphs.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Possibly related PRs

Merge Risk: Minimal · up to ccc6d

The reviewed transcript changes are mergeable after normal checks.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title clearly identifies the main change: label-based detection of voice-note headers that start turns. It is somewhat long and grammatically awkward, but it is specific and related to the changes…
Description check Passed The description includes all template sections, explains the bug and fix, identifies linked issues, marks the change types, documents verification, and describes migration impact. Two checklist items …
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 3 files. (1 skipped: 1 …
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title clearly identifies the main change: label-based detection of voice-note headers that start turns. It is somewhat long and grammatically awkward, but it is specific and related to the changeset.

Full details: Description check

Explanation

The description includes all template sections, explains the bug and fix, identifies linked issues, marks the change types, documents verification, and describes migration impact. Two checklist items remain unchecked, but the description is otherwise complete.

Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 3 files. (1 skipped: 1 unsupported.)

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Oct 26, 2026

Copy link
Copy Markdown

Reviewer's Guide

Fix Otter voice-note parsing by combining block detection with header-label continuity, ensuring time-shaped transcript lines remain words while recovering missing separators in text and Word exports; add comprehensive regression tests and document the resulting behavior and limits.

Sequence diagram for parsing ambiguous Otter voice-note lines

sequenceDiagram
    participant Input as Transcript
    participant Parser as turnsOf
    participant Blocks as startsBlock
    participant Labels as blockLabels
    participant Turns as HeadedTurns
    Input->>Parser: Read lines with voice-note headers
    Parser->>Blocks: startsBlock(lines, at)
    Blocks-->>Parser: Block-start headers
    Parser->>Labels: blockLabels(lines, voiceNoteLayout)
    Labels-->>Parser: Labels from block-start headers
    Parser->>Parser: Evaluate candidate header placement
    alt Candidate starts a block or label is known
        Parser->>Turns: Add headed turn
        Parser->>Turns: Append following line as words
    else Candidate has no matching block label
        Parser->>Turns: Append line as words
    end
Loading

Flow diagram for Otter voice-note header detection

flowchart TD
    A[Read transcript lines] --> B[Identify candidate headers]
    B --> C{Voice-note layout?}
    C -->|No| D[Apply existing header rules]
    C -->|Yes| E[Find headers that start blocks]
    E --> F[Collect labels from block-start headers]
    F --> G{Candidate starts a block?}
    G -->|Yes| H[Start a turn]
    G -->|No| I{Has label seen at a block start?}
    I -->|Yes| H
    I -->|No| J[Keep line as words]
    H --> K[Next line remains header words]
    J --> K
Loading

File-Level Changes

Change Details Files
Refine Otter voice-note header detection to use block position plus consistent speaker labels, preventing time-shaped words from being misclassified as new turns.
  • Replace the previous loose-header boolean with explicit place/label modes for ambiguous layouts.
  • Detect block starts from surrounding blank/word lines and collect labels from headers that start blocks.
  • Treat subsequent voice-note headers with labels already established by block-start headers as turns, while keeping unlabeled time-only layouts position-based.
  • Preserve the documented ambiguity where a newly labeled speaker header without a separating block cannot be distinguished from words.
src/cognition/library/transcripts/otter.ts
src/cognition/library/transcripts/word.ts
Expand regression coverage for voice-note text and Word exports across ambiguous layouts and documented limits.
  • Verify multiple time-ending lines remain words in one- and two-turn voice notes.
  • Cover missing blank paragraphs, no-empty-paragraph Word files, repeated Unknown labels, and Speaker 1/Speaker 2 exports.
  • Pin the unresolved case where a new speaker label appears without a block separator, plus time-only behavior.
src/cognition/library/__tests__/transcripts.test.ts
Update library documentation to describe label-assisted parsing and its ambiguity boundaries.
  • Document how voice-note headers are recognized when blank-line separators are missing.
  • Clarify handling of time-shaped words, repeated labels, speaker labels, time-only exports, and Word layouts.
  • Record the limitation that an unseparated, previously unseen speaker label may be read as words.
docs/features/LIBRARY_INDEX.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@jddunn
jddunn marked this pull request as ready for review October 26, 2026 05:02
@jddunn
jddunn requested a review from Victor-Evogor as a code owner October 26, 2026 05:02
@sourcery-ai

sourcery-ai Bot commented Oct 26, 2026

Copy link
Copy Markdown

Sorry @jddunn, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 12 hours and 25 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-26T05:04:11.512443Z ccc6dfd Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix Otter voice-note turn detection in text and Word transcripts

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Recognize voice-note headers with missing blank lines by matching labels from block-starting
 headers.
• Keep transcript lines ending in times within their turns instead of treating them as headers.
• Test text and Word exports, and document the remaining ambiguous-header cases.
Diagram

graph TD
  A["Text or Word"] --> B["Input lines"] --> C["Layout selection"] --> D{"Header kind"}
  D -->|"voice note"| E["Block labels"] --> F["Turn detection"] --> G["Transcript turns"]
  D -->|"time only"| F
  D -->|"other"| F
Loading
High-Level Assessment

Keep the label-based rule within the existing Otter layout parser. Blank-line counts cannot distinguish the demonstrated exports, and timestamp order cannot reliably distinguish headers from spoken words. The documented limit for unseen speaker labels is preferable to treating ordinary time-ending lines as turns.

Files changed (4) +208 / -69

Bug fix (1) +98 / -46
otter.tsIdentify voice-note turns using block-starting labels +98/-46

Identify voice-note turns using block-starting labels

• Separates label-based voice-note detection from the existing place-based time-only rule. Collects labels at block starts and uses them to recognize later headers while preserving the line immediately after a header as its words.

src/cognition/library/transcripts/otter.ts

Tests (1) +102 / -16
transcripts.test.tsCover ambiguous voice-note turns in text and Word exports +102/-16

Cover ambiguous voice-note turns in text and Word exports

• Adds cases for missing blank lines, multiple spoken lines ending in times, and Word files without empty paragraphs. Verifies distinct speaker labels, time-only behavior, and the known limit for a new speaker label outside a block.

src/cognition/library/tests/transcripts.test.ts

Documentation (2) +8 / -7
LIBRARY_INDEX.mdDocument label-based voice-note parsing and its limits +1/-1

Document label-based voice-note parsing and its limits

• Explains how block-starting header labels identify later voice-note turns without blank lines. Documents ambiguous distinct-speaker and time-only headers in Word and text layouts.

docs/features/LIBRARY_INDEX.md

word.tsClarify how Word paragraphs inherit Otter turn detection +7/-6

Clarify how Word paragraphs inherit Otter turn detection

• Updates the reader documentation to describe label-based parsing of voice notes without empty paragraphs and the resulting distinct-speaker ambiguity. The Word extraction behavior is unchanged.

src/cognition/library/transcripts/word.ts

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can type 'qodo, fix this' on a finding and the fix lands right on your PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@jddunn
jddunn merged commit 6333409 into master Oct 26, 2026
15 checks passed
@jddunn
jddunn deleted the fix/otter-reader-follow-up branch October 26, 2026 07:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant