Skip to content

chore: pin FluidAudio 0.15.4 → 0.15.5 (#122) - #142

Open
kiki830621 wants to merge 3 commits into
mainfrom
idd/122-fluidaudio-0155
Open

chore: pin FluidAudio 0.15.4 → 0.15.5 (#122)#142
kiki830621 wants to merge 3 commits into
mainfrom
idd/122-fluidaudio-0155

Conversation

@kiki830621

@kiki830621 kiki830621 commented Aug 2, 2026

Copy link
Copy Markdown
Member

Pins FluidAudio 0.15.4 → 0.15.5.

Refs #122

Why

0.15.5 completes the Parakeet Unified frontend — the CoreML preprocessor bundle is dropped in favour of a native Swift mel extractor, and the streaming encoder gains a per-latency-tier context suffix. That is what #123 needs.

TDT-ja was already reachable at 0.15.4. AsrModelVersion.tdtJa and the parakeet-0.6b-ja-coreml repo are present and byte-identical there, as are the Unified* managers. An earlier version of this PR said the models "exist only in 0.15.5" — that was wrong, and it puts #124's blocked by #122 premise in doubt along with it.

Accuracy: an identical transcript on a corpus that could have differed

The first version of this PR offered seven before/after pairs and concluded "Accuracy is unchanged". Verification found that table could not support the claim, for two independent reasons.

It was not a before/after comparison. The "0.15.4" column reconciles to store rows captured 2026-07-05/06 under app_version 0.10.0; the "0.15.5" column is the 2026-08-02 batch under 0.16.0 — four weeks and six bestASR versions apart. A fluid-* batch does exist in between (24 rows on 2026-07-19 under 0.14.0), and it shares zero corpora with the after-batch — which is what forced the older rows into the "before" column: the choice was constrained by overlap, not made carelessly. Issue #122 asks for a sweep on each side of the upgrade; only the after side was ever run.

And the corpora could not exercise what changed — though not for the reason the first write-up gave. ChunkProcessor carries five changes across two upstream commits: 7e856da4 adds the case-gated collapseSeamWordDuplicates and threads case-folding into the overlap matcher (changing where the seam is cut, not just what is removed after); 0ac0e414 adds three word-boundary fallbacks that are neither case- nor Latin-gated but are strictly conditional — upstream's own instrumentation reports 67 adversarial seams with zero fall-throughs. Of the seven removed rows, jfk (11.0 s) is single-chunk so the merger never runs; the three sensevoice and two paraformer rows never enter ChunkProcessor at all; only cv-zhtw-4 parakeet (25.68 s, two chunks) reaches it, and it confounds six bestASR versions. The table could not support its conclusion — but it was not empty, and the reason five of its rows carried no information was the ASR subtree they run in, not the absence of case.

So the table is removed rather than repaired, and replaced with a measurement that could have come out differently — the same tree built against each pin in turn, on an English corpus long enough to be multi-chunk:

pin corpus backend WER
0.15.4 (b9d43724) osr-harvard-1 (33.6 s, en) parakeet 0.037500
0.15.5 (19600a48) osr-harvard-1 (33.6 s, en) parakeet 0.037500

Bit-identical, not equal-after-rounding. Which code actually ran was settled on the binary, not the checkout: nm reports ChunkProcessor.caseVariantCanonicalIds present under the 0.15.5 pin and absent under 0.15.4. Neither the resolved version string nor the checked-out source settles it — a stale build artifact survives both.

What it establishes is narrower than "accuracy is unchanged", and narrower than an earlier draft of this section claimed. Byte-identical transcripts are themselves evidence that none of the five changes was reached: the collapse is case-gated and a clean reading passage has no case-variant seam duplicate; the fallbacks differ only where no splice-safe token exists, and any real entry would have kept content 0.15.4 dropped. So the run shows that a corpus whose shape makes all five reachable in principle comes out identical, with no evidence any was entered — a weaker claim than the first draft made and a stronger one than the removed table could support.

Raw outputs are committed at benchmarks/evidence/issue-122-fluidaudio-ab.json; the first version of this measurement existed only in a temp directory.

(Footnote on the old table, since it was not worthless: those seven pairs are in fact bit-identical in the store — the mismatched 2 dp / 1 dp rendering obscured it. That is real evidence those paths are deterministic and unmoved across six app versions. It is simply not evidence about this dependency bump.)

Compatibility: what compiling did and did not establish

#110 flagged DownloadUtils → ModelHub as breaking. It is not breaking for this repono compiled call site references either symbol, verified repo-wide rather than inferred from a green build. But two narrower claims in the first version of this PR were false:

  • The AsrModels signatures did change. Every public entry point took progressHandler: DownloadUtils.ProgressHandler? and now takes progressHandler: ProgressHandler?, as did every other consumed factory. Source-compatible here only because this repo never passes one — a different statement from "unchanged", and the issue's open question asked for the first.
  • The consumed surface is not "entirely high-level." The list omitted DiarizerModels.downloadIfNeeded(), AudioConverter.resampleAudioFile, DiarizerManager.initialize / .performCompleteDiarization, and AsrModelVersion. The first is a download-layer entry point, which undercuts the framing that the download-layer rewrite cannot reach this repo: it reaches it through a facade rather than by name.

Two unmentioned behaviour changes on call sites this repo does use, both benign: AsrModels.download now performs an extra fetch of the vocab JSON (upstream #748 — the weight manifest already pins both vocab files, so the newly-guaranteed fetch lands inside the pinned set), and performCompleteDiarization gained a defaulted trailing progressHandler.

Supply chain

Clean, and verified rather than assumed:

  • Tag → revision cross-checked against the GitHub API, not only the local mirror — a mirror fetched by this machine would faithfully reproduce upstream tampering. 19600a48… is the real v0.15.5.
  • swift package resolve against the committed tree is a no-op, so the lockfile is what SwiftPM computes rather than a hand edit.
  • Across a large upstream delta (41 commits, 156 files, a wholesale download-layer rewrite): no new transitive dependencies, no endpoint change (ModelRegistry is byte-identical between the tags), no TLS weakening, no new credential sink, no telemetry. The offline-egress gate got stronger, gaining per-request re-checks.
  • Weight pins confirmed empirically: hashing the live cache against the committed manifest gives 42/42 files clean across all three pinned repos.
  • Not re-pinning is deliberate. Re-running pin-weights.sh here would TOFU-launder any drift; the check above shows there was none to record. Leaving the manifest untouched is what preserved the ability to detect drift — please do not "fix" this reflexively.

Labelling corrections

The figures are error rates, not accuracy: values like 178 % are impossible under any bounded accuracy definition, and the earlier heading inverted the direction as well as the meaning. Two of the seven rows were fluid-paraformer, which the grid marks priority: 2, verified: false and release-sweep.sh excludes by default as "demoted for a known upstream decode bug" — stable numbers there evidence a stable bug, not preserved quality. And the comparability caveat said the two batches were "indistinguishable in the store" when they differ in app_version and decode_deterministic; it described a hypothetical confound while the real one went unstated. Throughput moved 161.6× → 126.5× on jfk (a 21.7 % single-run decline) — one uncontrolled run cannot attribute that to the dependency, but calling it "not evidence either way" was too strong.

Not established here

  • Diarization. The diarizer subtree moved substantially upstream (KMeansClustering, OfflineReconstruction, new ZeroVoteReembedder / OfflineEmbeddingExtractor / OfflineSortformerDiarizer) and this repo consumes diarization. No DER row exists on either side of the bump and scripts/validate-diarization.sh was not run, so that surface is unestablished rather than unchanged.
  • VAD manifest drift. weights-manifest.json still pins silero-vad-unified-256ms-v6.0.0; 0.15.5 moved to v6.2.1. Inert today (no VAD usage, no seam verifies that repo), recorded because it demonstrates the mechanism's blind spot: an upstream rename degrades a repo from "pinned" to "effectively unverified" without failing loudly, since WeightVerifier iterates manifest entries only and extra cache files never fail.
  • Schema provenance. A row still cannot be attributed to a dependency version from the store alone. The durable fix is a build_id or lockfile hash on the row — measurement schema 增 run_kind / decode-determinism provenance 欄位(follow-up from #109 verify) #111 / mlx-audio determinism 表示法抉擇:Bool?+nil vs per-backend enum vs document-only(deferred from #111) #118 territory, not this change's.

Tests

445 tests / 88 suites green. Pin, lockfile and weight manifest untouched by the correction commit.

Unblocks #123 / #124 — the Parakeet Unified and TDT-ja models exist
only in 0.15.5.

#110 flagged DownloadUtils → ModelHub as breaking. It is not breaking
for this repo: the rewrite is internal to the download layer and this
repo has zero references to it. Both questions the issue left open
were answered by compiling rather than by reading release notes — the
AsrModels signatures are unchanged, and the Chinese-family managers
used here are not the experimental zh-CN CTC / Qwen3 backends dropped
in 0.15.3.

Accuracy is unchanged: every overlapping before/after pair matches to
the precision the report prints, across parakeet, sensevoice and
paraformer. Throughput moved, but those are single unaveraged runs and
are not evidence either way.

Carrying #110's residue: the schema records app_version but not the
FluidAudio version, so the two batches are indistinguishable in the
store once written and are separated here only by capture time.

Refs #122
@kiki830621

Copy link
Copy Markdown
Member Author

Verify Report — PR #142

Engine

manual fan-out (3 lens Agents, model: opus, file-based output) + Codex (gpt-5.6-sol, effort xhigh, via codex-call HTTPS). 4 verifiers, two model families — not the usual 6. See Process Gaps.

Two deliberate deviations from the default ensemble, both stated rather than silently applied:

  • The logic lens was not dispatched. The diff is two version strings plus CHANGELOG prose; there is no logic in it to review, and running the lens anyway would have produced noise while inflating the appearance of rigor. The security lens was kept and is the load-bearing one here — a dependency pin is a supply-chain decision, and an exact: pin plus a changed revision hash is a real surface.
  • Backend: canonical pai-ensemble 2.20.0 was available; this session operates under a standing instruction not to launch Workflow runs unprompted, so the skill's declared quality-equivalent manual path was used.

Diff-freshness gate (#228): PASS — frozen bd35b9c == PR head at aggregate time, re-confirmed after each stage.

Process Gaps

The Devil's Advocate leg did not run. Two attempts, neither produced output:

  1. First instance completed its analysis (six analysis scripts survive in its isolated workdir) but died on ECONNRESET before writing findings.
  2. Retry, with full context re-paste per the recovery protocol, hit the account session limit.

Per the skill's second-idle fallback, the coordinator performed the adversarial review in its place and the substitution is recorded here rather than absorbed. This is a lower-quality substitute and should be read as one: the coordinator had already read all four findings files before reviewing, so it lacks the cognitive isolation the role depends on — an adversarial pass works because it does not share the other reviewers' framing, and this one does. The coordinator's contributions are marked [coordinator] below.

Two findings that a real adversarial pass would likely have interrogated further, and did not get to: whether the six bestASR versions between the two batches contain anything that touches the fluid-* path at all (which bears directly on how much of the confound in F1 is nominal), and whether security's PASS holds on axes it did not enumerate.

Working-tree contention. One lens ran a before/after warning comparison by flipping Package.swift to 0.15.4 and back while others were reading the tree. Two of the three lenses detected this independently, switched to git show HEAD: reads, and said so. Verified after every stage and at publication: git status --porcelain empty, pin = 0.15.5, HEAD = bd35b9c. No finding rests on a mutated tree.

Aggregate

FAIL — 2 blocking, 6 follow-up.

The dependency change itself is clean; the security review is a substantive PASS with rigorous method. What fails is the PR's account of itself: three of its factual claims are false as written, and its headline evidence cannot support its headline conclusion.

Requirements coverage — issue #122

# Requirement Status
1 Pin → 0.15.5, swift build green, all tests green MET — build clean, 445 tests / 88 suites pass, matching the PR body exactly
2a Verify whether AsrModels.download / .load signatures changed ⚠️ met in substance, reported incorrectly — they did change; compatible, but the record says "unchanged" (F3)
2b Verify ChineseFamilyEngine does not depend on the zh-CN CTC / Qwen3 backends dropped in 0.15.3 MET — verified true, though the bump could not have tested it: 0.15.3 predates the pin this PR moves from, so a green build at 0.15.4 had already answered it
3 Run a release sweep before AND after the upgrade NOT MET — only the after-sweep exists (F1)

Findings (merged, deduped; the stacking effect is collapsed — see the note below the table)

# Severity Finding Source Action
F1 HIGH The accuracy evidence cannot support "Accuracy is unchanged", for two independent reasons. (a) No pre-upgrade sweep was run. Every value in the "0.15.4" column reconciles to store rows from 2026-07-05/06 under app_version 0.10.0; the "0.15.5" column is the 2026-08-02 batch under 0.16.0. The store holds no fluid-* batch between 2026-07-19 and the post-bump run. Issue #122's Expected item 3 asks for a sweep on each side. (b) The corpora measured cannot exercise what changed. 0.15.5 adds seam-duplicate collapse to the Parakeet TDT chunk merger (ChunkProcessor.swift:581-596, :701-733, :736+), reachable from this repo's call chain but firing only on multi-chunk audio (chunk ~14.96 s, overlap 2.0 s — both confirmed at ChunkProcessor.swift:25-28) whose seam duplicate is case-differing Latin text (:806-807). The two parakeet evidence rows are jfk at 11.000000 s (single chunk — the branch is never entered) and a Chinese corpus (no case — the predicate can essentially never hold). The English multi-chunk corpora that would exercise it are already on disk and unmeasured: osr-harvard-1/2/3 (33 s), librispeech-devclean/testclean (32–71 s). requirements + regression + Codex Blocking
F2 HIGH Three factual claims are false or unsupportable as written, in the permanent record. (i) "models exist only in 0.15.5"AsrModelVersion.tdtJa and Repo.parakeetJa are present in 0.15.4 and byte-identical; the Unified managers are all present too. What changed is the Unified frontend (CoreML preprocessor dropped for native Swift mel; streaming encoder gained a context suffix), i.e. the loading path, not availability. There is a genuine reason to prefer 0.15.5 for #123 — just not the stated one — and this puts #124's "blocked by #122" premise in doubt. (ii) "the AsrModels signatures are unchanged" — every public entry point changed its progressHandler parameter type (DownloadUtils.ProgressHandlerProgressHandler), as did every other consumed factory. Source-compatible here only because this repo never passes one. (iii) "the consumed surface is entirely high-level" — the enumeration omits DiarizerModels.downloadIfNeeded(), AudioConverter.resampleAudioFile, DiarizerManager.initialize / performCompleteDiarization, and AsrModelVersion. The first is a download-layer entry point, which undercuts the framing that the download-layer rewrite cannot reach this repo. requirements + regression + Codex Blocking
F3 MEDIUM Two unmentioned behaviour changes on APIs this repo calls: AsrModels.download now performs an extra network fetch of the vocab JSON (ensureVocabularyDownloaded, upstream #748), and DiarizerManager.performCompleteDiarization gained a defaulted trailing progressHandler. Both benign — the weight manifest already pins both vocab files, so the newly-guaranteed fetch lands inside the pinned set — but they sit on call sites the PR describes as unchanged. requirements + regression In-scope fix (doc)
F4 MEDIUM The comparability caveat is false about the data it names. It says the two batches are "indistinguishable in the store once written… separated here only by capture time" — but they carry different app_version (0.10.0 / 0.16.0) and different decode_deterministic (null / flag-not-consumed), so they are trivially separable, and measured_at is itself a stored column. The general point behind it is correct and important (the schema has no FluidAudio-version field, verified against StoreTables.swift), but as written the sentence describes a hypothetical confound while the actual one — six bestASR versions — goes unstated. hf_revision does not close the gap: it is a static per-row value declared in ModelGrid.swift and the fluid-* rows do not carry it. requirements + Codex In-scope fix
F5 MEDIUM The table is headed "Accuracy is unchanged" but its values are error rates — 178.87 % and 181.94 % cannot be accuracy under any bounded definition. This inverts the direction as well as the meaning: for an error rate, larger is worse. Related, and noticed by no lens [coordinator]: two of the seven rows are fluid-paraformer, which the grid marks priority: 2, verified: false (ModelGrid.swift:85-87) and which the sweep script excludes by default as "demoted for a known upstream decode bug". Stable numbers from a known-broken backend evidence that the bug is stable, not that quality is preserved — and their presence means the after-run used --all-grid, which is not stated. Codex + [coordinator] In-scope fix
F6 MEDIUM Presentation overstates the resolution of the comparison. The "0.15.4" column is rendered at 2 dp and the "0.15.5" column at 1 dp, while BenchmarkReport.swift:45 prints at 1 dp — so "identical to the precision the report prints" is true only of the right-hand column, and 93.55 vs 93.5 invites reading a 0.05 pp movement that does not exist. (The underlying values are in fact exactly equal — see the adjudication below — which the table's own formatting obscures.) requirements + Codex In-scope fix
F7 MEDIUM Diarization changed substantially upstream — KMeansClustering, OfflineReconstruction, new ZeroVoteReembedder / OfflineEmbeddingExtractor / OfflineSortformerDiarizer — and this repo consumes diarization at DiarizationEngine.swift:39-43 and SpeakerEnroller.swift:25-29. There is no DER row in the evidence and no sign that scripts/validate-diarization.sh was run. Unestablished rather than disproven. regression Follow-up
F8 MEDIUM Eight live in-tree references still name 0.15.4, including ModelGrid.swift:51"SwiftPM exact: 0.15.4 is the supply-chain anchor" — which now contradicts Package.swift, and openspec/specs/chinese-asr-engines/spec.md:24, a live spec making a normative statement about a version no longer pinned. Two of the stale comments are behavioural, not cosmetic (ParakeetEngine.swift:172 "0.15.4 returns duration 0", guarding a live fallback; ModelGrid.swift:78 the stated reason paraformer sits at priority 2), so they may now be describing dead code. The openspec/changes/archive/** and historical CHANGELOG hits are correctly frozen and must not be "fixed". security + requirements In-scope fix
F9 LOW weights-manifest.json still pins silero-vad-unified-256ms-**v6.0.0**; 0.15.5 moved to v6.2.1. Inert today (bestASR has no VAD usage and no seam verifies that repo). Recorded because it demonstrates the control's blind spot: WeightVerifier.verify iterates manifest entries only and extra cache files never fail, so an upstream rename silently degrades a repo from "pinned" to "effectively unverified" — on a warm cache the old files remain, verification reports .verified, and the artifact actually loaded is the new unpinned one. A rename is the one upstream change shape this control cannot see. Costless now; not if a rename ever hits parakeet-tdt-0.6b-v3 or speaker-diarization. security Follow-up
F10 LOW No store row in the entire 382-row store carries run_kind, although scripts/release-sweep.sh:149-150 passes --run-kind release-sweep. So neither batch is identifiable as a release-sweep run, and issue #122 asked specifically for one. Either the plumbing has a gap or the script was never the source; worth resolving either way. requirements Follow-up
F11 INFO Cross-PR observation [coordinator]: the after-batch contains parakeet rows on Chinese corpora at ~95–104 % error. Parakeet's grid row advertises European languages only — so the sweep that produced this PR's evidence was running exactly the configuration that PR #141 exists to warn about, and whose warning was invisible on the default path at the time it ran. [coordinator] Follow-up

On the stacking effect. Three lenses plus the cross-model leg converged hard on "the documentation is false", which makes the count look larger than the problem. The genuinely distinct claims are collapsed above: "models only in 0.15.5", "signatures unchanged" and "consumed surface entirely high-level" are one finding (F2) reported by three sources, not three findings. The evidence problems, by contrast, really are separate — F1(a) is provenance and F1(b) is coverage, and closing one leaves the other open.


Adversarial adjudication [coordinator] — two findings the ensemble missed, pulling in opposite directions

I queried the measurement store directly rather than relying on the lens reports, and found two things none of the four sources reported. They matter because they cut against each other.

A. The seven pairs are bit-identical in the raw store, not merely equal after rounding.

corpus backend 2026-07-05/06 (app 0.10.0) 2026-08-02 (app 0.16.0)
59dfb9a4acb3 (jfk) parakeet 0.0000 % 0.0000 %
29007d2e6418 parakeet 93.5484 % 93.5484 %
33dd468f27c6 sensevoice 14.0845 % 14.0845 %
af3571b78130 sensevoice 47.2222 % 47.2222 %
29007d2e6418 sensevoice 11.2903 % 11.2903 %
33dd468f27c6 paraformer 178.8732 % 178.8732 %
af3571b78130 paraformer 181.9444 % 181.9444 %

This is stronger than the CHANGELOG's own table implies. The cross-model leg's objection — that two differently-rounded numbers cannot establish equality — is correct about what the table shows and wrong about what the data holds.

B. The 2026-07-19 batch (app 0.14.0, 24 rows) shares zero corpora with the after-batch.

Its corpus IDs (12669b3d0de8, 604acbc2d096, c4120b0b7d67, …) and the after-batch's (59dfb9a4acb3, 33dd468f27c6, af3571b78130, 29007d2e6418) do not intersect at all. So the choice of the older, smaller 07-05/06 batches as "before" was forced by overlap, not carelessness — a fact that reads as an implicit accusation in one lens report and should not.

Adjudication. A defuses part of F1(a) and does nothing for F1(b).

The bit-identity is real evidence that the decode paths those corpora exercise are deterministic and unmoved — across six bestASR versions as well as across the FluidAudio bump. So "the comparison confounds six bestASR versions" overstates the practical damage: the confound existed and demonstrably did not materialise. On that basis I record F1 at HIGH rather than CRITICAL, which is a downgrade from the requirements lens's rating.

But it rescues nothing on F1(b), and arguably sharpens it. If the changed branch cannot fire on single-chunk audio or on a language without case, then bit-identity on exactly those corpora is the predicted outcome whether or not 0.15.5 changed anything — it is not evidence about the change, it is evidence about the absence of the change's preconditions. Identity where the code cannot differ says nothing about where it can.

So both statements are true at once, and the honest form of the claim is narrower than either the PR's or the harshest lens reading:

On the seven overlapping corpus/backend pairs, error rates are bit-identical between the 2026-07-05/06 baseline (bestASR 0.10.0) and the post-bump sweep (bestASR 0.16.0). None of those pairs exercises 0.15.5's new seam-duplicate collapse, which requires multi-chunk audio containing case-differing Latin text; the English multi-chunk corpora that would are on disk and were not measured. No pre-upgrade sweep was run, and the store cannot attribute a row to a FluidAudio version.

On the caveat (F4) I also differ from the requirements lens, which rated it CRITICAL partly on the reading that it conceals the real confound. The sentence is false about the pair it names and that is worth correcting, but it is false in the direction of understating the author's own diligence gap rather than overstating a quality claim. MEDIUM.


What the security review establishes (and it is substantial)

Recorded because a FAIL verdict should not flatten a genuinely clean result on the axis where this PR is actually risky.

The pin is correct and the supply-chain posture is unchanged or better. Tag → revision was cross-checked against the GitHub API rather than the local mirror alone, on the stated reasoning that a mirror fetched by this machine would faithfully reproduce upstream tampering — 19600a48… is the real v0.15.5. swift package resolve against the committed tree is a no-op, so the lockfile is what SwiftPM computes rather than a hand edit. Across a large upstream delta (41 commits, 156 files, a wholesale rewrite of the download layer), there are no new transitive dependencies (FluidAudio declares none, and its Package.swift is byte-identical between tags), no network-endpoint change (ModelRegistry is byte-identical), no TLS-verification weakening, no new credential sink, no telemetry — and the offline-egress gate got stronger, gaining per-request re-checks inside FileDownloader and HFTreeLister.

Weight pins were confirmed empirically rather than assumed: hashing the live cache against the committed manifest gives 42/42 files clean across all three pinned repos. And the PR is correct not to re-pin, which is worth stating so a later reviewer does not "fix" it reflexively — re-running the pin script here would have TOFU-laundered any drift, and the check above shows there was none to record. Leaving the manifest untouched is what preserved the ability to detect drift.

Scope check

Clean. Three files, +43/−4. The re-cut that moved Package.swift / Package.resolved here from PR #140 left no inconsistency: CHANGELOG text, manifest pin and lockfile revision now agree, the ### Changed block sits correctly in the unreleased section, and nothing from the old split leaked through. The blocker relationship to #110 is genuinely lifted — ModelGrid still carries only 0.6b-v3, which is correct, since adding tdt-ja / unified rows is #123/#124's work, and an unmapped grid model throws loudly rather than falling back silently.

Next

F1 and F2 gate the merge. F1(b) closes at the cost of a single benchmark run on one English multi-chunk corpus (osr-harvard-1, 33 s); F1(a) closes either by checking out 0.15.4 for a real before-sweep, or by relabelling the table with both app versions and capture dates and weakening the conclusion to the honest form above. F2 is three sentence-level corrections to the PR body and CHANGELOG.

Verify was run at bd35b9c; re-run after the blocking findings are addressed — and note that the Devil's Advocate leg owes this PR a pass it did not get.

…122)

Verification found the dependency change itself clean and the entry
describing it wrong in four places. The pin, the lockfile revision and the
supply-chain posture are unchanged by this commit; only CHANGELOG.md is.

The accuracy evidence could not support its conclusion. 0.15.5 adds
seam-duplicate collapse to the Parakeet TDT chunk merger, and that branch
fires only on audio longer than one ~14.96s chunk AND only when the seam
duplicate is case-differing Latin text. The two parakeet rows offered as
proof were jfk (11.0s, single chunk, the branch is never entered) and a
Chinese corpus (no case, so the predicate can essentially never hold).
Both are structurally blind to the only behavioural change on that path:
identical numbers there were the predicted outcome whether or not
anything changed.

Measured on a corpus that CAN exercise it -- osr-harvard-1, 33.6s of
English -- by building the same tree against each pin in turn:

  0.15.4 (b9d43724)  osr-harvard-1  parakeet  WER 0.037500
  0.15.5 (19600a48)  osr-harvard-1  parakeet  WER 0.037500

Bit-identical, not equal-after-rounding. The 0.15.4 side was confirmed
genuinely 0.15.4 by the ABSENCE of collapseSeamWordDuplicates from the
checked-out source, which is stronger than the resolved version string: a
stale build artifact can survive a pin change, but code that is not on
disk cannot run.

The earlier table was not a before/after comparison at all and is removed
rather than repaired. Its "0.15.4" column reconciles to store rows from
2026-07-05/06 under app_version 0.10.0; its "0.15.5" column is the
2026-08-02 batch under 0.16.0 -- four weeks and six bestASR versions
apart, with no fluid-* batch in between. Issue #122 asked for a sweep on
each side of the upgrade; only the after side was ever run. The seven
pairs are in fact bit-identical in the store, which the mismatched 2dp/1dp
rendering obscured -- real evidence that those paths are deterministic
across six app versions, and simply not evidence about this bump.

Three factual claims corrected:

- "models exist only in 0.15.5" is wrong for TDT-ja. AsrModelVersion.tdtJa
  and the parakeet-0.6b-ja-coreml repo are present and byte-identical in
  0.15.4, as are the Unified managers. What 0.15.5 completes is the
  Unified FRONTEND (native Swift mel replacing the CoreML preprocessor,
  per-latency-tier streaming encoder). There is a genuine reason to prefer
  0.15.5 for #123 -- just not the stated one -- and the error put #124's
  "blocked by #122" premise in doubt with it.
- "the AsrModels signatures are unchanged" is false: progressHandler's
  type changed on every public entry point and on every consumed factory.
  Source-compatible here only because this repo never passes one, which is
  a different statement, and the issue's open question asked for the first.
- "the consumed surface is entirely high-level" omits
  DiarizerModels.downloadIfNeeded(), AudioConverter.resampleAudioFile,
  DiarizerManager.initialize / .performCompleteDiarization, and
  AsrModelVersion. The first is a download-layer entry point, which
  undercuts the framing that the rewrite cannot reach this repo.

Also corrected: the figures are error rates, not "accuracy" (178% is
impossible under any bounded accuracy definition, and the heading inverted
the direction as well as the meaning); two of the seven rows were
fluid-paraformer, which the grid marks priority 2 / verified false and the
sweep excludes by default as a known upstream decode bug, so stable
numbers there evidence a stable bug rather than preserved quality; the
comparability caveat said the two batches were "indistinguishable in the
store" when they differ in app_version and decode_deterministic --
describing a hypothetical confound while the real one went unstated; and
calling the 21.7% throughput decline "not evidence either way" was too
strong, since one uncontrolled run cannot attribute a difference but does
observe one.

Recorded as unestablished rather than unchanged: the diarizer subtree also
moved substantially upstream and this repo consumes diarization, but no
DER row exists on either side and validate-diarization.sh was not run.

445 tests / 88 suites green. Pin, lockfile and weight manifest untouched.

Refs #122
Round-2 verification found the previous correction had fixed three false
claims and introduced two more, and that my first attempt at fixing THOSE
introduced a third. This is the third consecutive round on this entry
where a correction claimed more than its evidence; the changes here are
mostly subtraction.

"with no fluid-* batch in between" was false. The store holds 24 fluid-*
rows on 2026-07-19 under app_version 0.14.0, squarely in between. Worse,
the accurate fact it displaced was the one that defended the choice: that
batch shares ZERO corpora with the after-batch, which is why the older
07-05/06 rows were used as "before" — constrained by overlap, not
careless. I had established that in round 1 and compressed it into a
falsehood.

"the only behavioural change on that path" was also false, and the
recount that replaced it was still short. ChunkProcessor carries FIVE
changes across two commits: 7e856da4 adds the case-gated
collapseSeamWordDuplicates AND threads case-folding into the overlap
matcher — tokensMatch went from exact token-ID equality to case-folded
equality, which changes where the seam is CUT rather than what is removed
after it — and 0ac0e414 adds three word-boundary fallbacks.

The correction to that then overstated in the other direction, asserting
the fallbacks "run on any multi-chunk audio" and were therefore
exercised. They are strictly conditional, differing from 0.15.4 only
where no splice-safe token exists at the seam. Upstream's own
instrumentation, in that commit message, reports ~67 chunk-merge events
over ~15 minutes of deliberately adversarial agglutinative audio hitting
the guarded logic on ~70% of seams and never falling through to any of
the three. Asserting the enclosing function ran is not asserting the
changed branch was taken — the same category error the original
"structurally blind" sentence made.

So the headline is wrong and is replaced. Byte-identical transcripts are
themselves evidence that NONE of the five changes was reached. What the
run establishes is that a corpus whose shape makes all five reachable in
principle comes out identical, with no evidence any was entered. Weaker
than "accuracy is unchanged"; stronger than the removed table could
support.

"code that is not on disk cannot run" was unsound — a stale build
artifact survives a pin change and survives the checkout too. The artifact
to interrogate is the binary: nm reports
ChunkProcessor.caseVariantCanonicalIds present under the 0.15.5 pin and
absent under 0.15.4. The conclusion holds; the argument did not, and it
had been copied into the evidence file as well.

Also corrected: "as are the Unified* managers" claimed byte-identity for
five files of ~300 changed lines, in the same paragraph that says 0.15.5
rewrites their frontend.

Beyond the record: the A/B wrote two rows into the local measurement
store, and the 0.15.4-side row was stamped app_version 0.16.0 — the
version shipping with 0.15.5 — because the schema has no dependency
field. That is the first real instance of the confound this very entry
documents, created by the run that documents it. The row was removed
(backup retained) and the measurement now lives in
benchmarks/evidence/issue-122-fluidaudio-ab.json, which also fixes the
headline number being reproducible only from a temp directory.

Nine live in-tree 0.15.4 references are updated. Version pointers move to
0.15.5; the two BEHAVIOURAL claims (duration-0 fallback, paraformer BPE)
keep their 0.15.4 attribution and now say they were not re-verified at
0.15.5, because bumping the number there would assert something unchecked.

445 tests / 88 suites green. Pin, lockfile and weight manifest untouched
by this commit.

Refs #122
@kiki830621

Copy link
Copy Markdown
Member Author

Verify Report — PR #142, Round 2

Re-verify after the round-1 blocking findings were addressed. This report describes 69916b8; the fixes it prompted are in 9172f4b, summarised at the end.

Engine — degraded, and it matters

4 verifiers, not 6. 2 lens Agents (opus) + Devil's Advocate + coordinator. The cross-model leg was unavailable: codex-call returned HTTP 429 usage_limit_reached (pro plan, ~5 days to reset). Not a transient network failure and not retried, because retrying cannot succeed inside the window.

That gap is worth stating rather than noting, because in round 1 the cross-model leg produced this PR's two sharpest findings — that a table of 178 % values cannot be headed "Accuracy" (they are error rates, and the heading inverted the direction as well as the meaning), and that comparing 93.5 against 93.55 at mismatched precision cannot establish equality. Neither came from a Claude lens. The asymmetry appears to be structural: Codex receives the diff and no repository, so it interrogates whether the text is self-consistent, while the Claude lenses, having repo access, go after behaviour. Round 2 had no such counterweight. The Devil's Advocate was told so and made a deliberate pass at that category; it found three, so the gap was real rather than theoretical.

As in round 1, the logic lens was deliberately not dispatched — the diff is prose.

Diff-freshness gate (#228): PASS at 69916b8.

Process note — a third party was editing the tree

Mid-review, an unrelated branch (idd/144-diarization-knobs, two commits at 16:28–16:29) was created and the working tree switched to it. Consequences, all now repaired: the coordinator's correction commit landed on that branch rather than this one; a one-line edit of DiarizationEngine.swift belonging to this PR was swept into the #144 commit by a git add over a tree containing it; and swift test failures observed during the window belonged to #144, not here.

The Devil's Advocate independently detected the tree moving ("at least five times… several sibling conclusions were true when written and are not true now") and filed it. Both it and the coordinator attributed the movement to the coordinator's own edits. Neither checked who else was present. Recorded because git status --porcelain was empty at every checkpoint — a branch switch does not dirty the tree, so the discipline this skill prescribes cannot see it.

Aggregate

FAIL at 69916b8 — 3 blocking. The dependency change remains clean; what fails is, for the third consecutive round, the account of it.


Round-1 findings — disposition

R1 status at 69916b8
F1 (HIGH, blocking) — the accuracy evidence could not discriminate CLOSED at the time, then re-opened by D3. The old table is removed rather than repaired and a real A/B replaces it; both lenses reproduced the numbers independently, one in a fresh clone with a per-pin build. But the replacement's own claim about what the run exercised does not hold — see the adjudication.
F2 (HIGH, blocking) — three false factual claims CLOSED. All three corrected in both artifacts, and each replacement independently verified true.
§3 (R1 regression lens, HIGH) — the regression gate is WhisperKit-only, zero FluidAudio coverage, undisclosed OPEN — and it never reached the author. This finding was in the round-1 regression lens's report and is absent from the round-1 master findings table. The merge step lost it.
F3–F6 CLOSED, each verified against the artifact rather than the commit message
F7 (diarization DER), F9 (VAD manifest) DEFERRED, honestly worded as unestablished rather than unchanged
F8 — nine live in-tree 0.15.4 references, marked "in-scope fix" OPEN, unfixed, unmentioned
F10 — run_kind absent from all 384 store rows OPEN and re-instantiated — the A/B's own rows carry none either

No finding was falsely claimed as fixed. Both lenses checked every closure against the artifact.


The adjudication: what the A/B establishes

Round 1 said the evidence could not exercise the changed path. The correction ran a real A/B — the same tree built against each pin, on osr-harvard-1 (33.6 s, English, multi-chunk) — and got byte-identical WER. Both lenses reproduced it. That is genuine progress over a table that compared two batches four weeks and six app versions apart.

But three successive statements about what changed have each been wrong, in different directions.

Round 1's write-up named one behavioural change (the case-gated collapseSeamWordDuplicates) and wrote off all seven rows on that basis. The correction named two commits and asserted the second one's fallbacks were exercised. Both under-count, and the second overstates.

The Devil's Advocate established the actual surface: five changes across two commits. 7e856da4 contributes two, not one — caseVariantCanonicalIds feeds the post-merge collapse and is threaded into the overlap matcher, where tokensMatch moved from exact token-ID equality to case-folded equality. That second one changes which token pairs anchor the merge, i.e. where the seam is cut, which is a different kind of change from removing a duplicate afterwards, and it went unnamed in every draft. 0ac0e414 contributes three word-boundary fallbacks.

And the claim that those fallbacks were exercised is false for the same reason the original "structurally blind" sentence was: it conflates the enclosing function running with the changed branch being taken. All three are strictly conditional, differing from 0.15.4 only where no splice-safe token exists at the seam. Two independent lines of evidence say none fired:

  • The A/B's transcripts are byte-identical under both pins. Worked backwards: any non-degenerate entry would have kept content 0.15.4 dropped, so the transcripts would differ.
  • Upstream's own instrumentation, in 0ac0e414's commit message, which no draft quoted: ~67 chunk-merge events over ~15 minutes of deliberately adversarial agglutinative audio hit the guarded logic on ~70 % of seams and never fell through to any of the three. "Runs on any multi-chunk audio" is not a defensible reading of that.

So the honest position is narrower than either draft: the run shows that a corpus whose shape makes all five changes reachable in principle — unlike jfk or a caseless one — produces an identical transcript, with no evidence that any of them was entered. That is weaker than "accuracy is unchanged" and stronger than the removed table could support.

This re-opens F1(b) in weakened form, overturning the lenses' "CLOSED".


Findings (merged)

# Severity Finding Source
R2-A HIGH (blocking) The entry asserted the fallbacks were exercised. Nothing shows that, and the transcripts plus upstream's own instrumentation say they were not. The headline "no change detected on the path 0.15.5 actually changes" claims more than the run can carry. DA
R2-B HIGH (blocking) "with no fluid-* batch in between" is false. 24 fluid-* rows sit at 2026-07-19 under 0.14.0. Worse, the accurate fact it displaced was the one that defended the choice: that batch shares zero corpora with the after-batch, which is why the older rows were used. The compression both stated a falsehood about the project's own data and discarded the exculpating fact — established in round 1 by the coordinator, then compressed away. requirements, upheld by DA
R2-C HIGH (blocking) The correction landed in the CHANGELOG only; the PR body carried both false claims verbatim. This is the exact failure mode round 1 caught on #140 — and which the round-2 regression lens had praised this PR for not repeating. DA
R2-D MEDIUM The change surface is under-counted even after two rewrites: the overlap matcher's case-folding (tokensMatch) is a behavioural change to where the seam is cut and is named nowhere. Five changes, two commits. DA
R2-E MEDIUM The A/B wrote two mutually indistinguishable rows into the durable measurement store, and the 0.15.4-side row is stamped app_version 0.16.0 — the version shipping with 0.15.5 — because the schema has no dependency field. This is the first real instance, in 384 rows, of the confound this very entry documents in the abstract. Created by the run that documents it, and unmentioned. regression
R2-F MEDIUM "code that is not on disk cannot run" is unsound. A stale build artifact survives a pin change and survives the checkout too; the artifact to interrogate is the binary. The conclusion holds — independently confirmed via nm — but the rigor claim did not, and it had been copied into the new evidence file, so the unsound argument existed in two places. requirements
R2-G MEDIUM "…as are the Unified* managers" claims byte-identity for five files of ~300 changed lines, in the same paragraph that says 0.15.5 rewrites their frontend. requirements
R2-H MEDIUM A round-1 HIGH never reached the master findings table — the regression gate being WhisperKit-only, with zero FluidAudio coverage, undisclosed. The synthesis step lost it, so it was never actioned. regression
R2-I MEDIUM F8 (nine live 0.15.4 references, including ModelGrid.swift:51 which declares the supply-chain anchor and now contradicts Package.swift, and a live normative spec line) was marked "in-scope fix" in round 1 and left untouched. requirements + regression
R2-J LOW The A/B's raw outputs existed only in a temp directory, so the headline measurement was not reproducible from the repo — the same gap round 1 recorded against the original sweep. regression
R2-K LOW Two wrong sub-claims inside the progressHandler correction. requirements
R2-L LOW The collapse predicate provably never fired on osr-harvard-1, which is worth stating alongside "a corpus that can exercise it". requirements
R2-M INFO "~14.96 s chunk" is the non-default branch's value; the default is 14.88 s. No downstream consequence. requirements

Negative space — attacks that failed

  • Did the A/B damage the pin? No. Both .bak files were diffed byte-for-byte against git show HEAD:; Package.swift, Package.resolved and weights-manifest.json are exactly as committed, the checkout is back at v0.15.5, and the reflog shows no stray commits. The durable residue was in the measurement store, not the tree.
  • "Only CHANGELOG changed" — confirmed by git show --name-only.
  • Are the three round-1 corrections substantively true? Each was re-derived independently: TDT-ja's presence in 0.15.4, the progressHandler type change, and the four omitted consumed APIs.
  • Is the A/B reproducible? Yes — one lens re-ran it in a fresh clone with a per-pin build and got the same numbers. (It also hit an unrelated swift build -c release compiler crash in swift-transformers and correctly reasoned that WER is decided by the CoreML graphs and merge logic, not by -O.)
  • PR body ↔ CHANGELOG consistency — checked and reported clean by the regression lens, and it was clean for round 1's corrections. R2-C is about the round-2 corrections, which post-date that check.

What changed in response (9172f4b)

  • The headline is replaced with what the run establishes, and the scope paragraph now says explicitly that byte-identical transcripts are themselves evidence that none of the five changes was reached (R2-A, R2-L).
  • The change surface is counted correctly — five changes, two commits, including the overlap matcher's case-folding and upstream's own zero-fall-through instrumentation (R2-D).
  • "No fluid-* batch in between" is corrected, and the exculpating fact restored (R2-B).
  • The PR body carries all of it (R2-C).
  • The store row was removed — the 0.15.4-side row that could not say what condition produced it — with a backup retained, and the measurement now lives in benchmarks/evidence/issue-122-fluidaudio-ab.json (R2-E, R2-J).
  • The unsound "not on disk" argument is replaced in both places by the nm check on the binary (R2-F).
  • The Unified* byte-identity claim is corrected (R2-G).
  • The nine stale references are updated — version pointers to 0.15.5; the two behavioural claims (the duration-0 fallback, the paraformer BPE note) keep their 0.15.4 attribution and now record that they were not re-verified at 0.15.5, because bumping the number there would assert something unchecked (R2-I).

445 tests / 88 suites green. Pin, lockfile and weight manifest untouched.

Not addressed, and now explicit: R2-H — the regression gate has no FluidAudio coverage at all, so it cannot detect this class of change; the finding was lost in round 1's synthesis and belongs in a follow-up issue rather than in a dependency bump. Also deferred: diarization DER, the VAD manifest rename, run_kind absence, and the schema's inability to attribute a row to a dependency version.

Re-verify at 9172f4b before merge — and note that any re-verify inside the next five days will again be missing the cross-model leg.

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