Repair stale transcription outputs when resuming

This commit is contained in:
2026-09-22 00:50:51 +00:00
parent 96b4e73f53
commit 54d335c820
6 changed files with 245 additions and 1 deletions

View File

@@ -62,7 +62,11 @@ timeout, credentials, and diagnostic locations are operational and do not
change this evidence.
An ordinary retry reuses every valid speaker checkpoint even when aggregate
transcribe status is failed, running, or stale. `--force` first durably replaces
transcribe status is failed, running, or stale. A succeeded stage is skipped only
when its stored speaker set and aggregate output evidence also match the current
collection. Otherwise, execution repairs those records without retranscribing
reusable speakers and stales dependents through the normal runner path.
`--force` first durably replaces
the collection with an empty versioned collection, so a later ordinary retry
cannot resurrect pre-force results; speakers checkpointed by that forced
attempt remain reusable.

View File

@@ -0,0 +1,96 @@
# Transcription Checkpoint Review Remediation
Status: Open.
Reviewed commits `7b0bf99` through `96b4e73` against the speaker-checkpoint and
upload-replay roadmap and the original user request. No production changes were
made during review. The two failures below were reproduced with temporary tests
using the existing stage fixtures; those temporary tests were removed afterward.
## 1. Reconcile Aggregate State Even When No Speaker Needs HTTP Work
Priority: P2. Status: Complete.
Implemented read-only aggregate identity/evidence checks before resume skips.
Regression tests cover removed inputs, missing/duplicate/mismatched outputs,
repair without HTTP requests, downstream invalidation, and exclusion of removed
speakers from merge without deleting their files. Equivalent path forms and
output ordering remain reusable. Focused app/stage tests and documentation
checks pass.
Location: `internal/stage/transcribe_resume.go:21` and the reconciliation result
consumed there.
`ValidateResume` returns reusable whenever `reconciliation.work` is empty. That
only establishes that the remaining speakers need no transcription; it does
not establish that the stored speaker collection and aggregate outputs match
the current input set. Removing a speaker while the remaining inputs are
unchanged produces an empty work list, so a succeeded record can be skipped
without pruning the removed speaker. Its old aggregate output can then remain
available to merge. Likewise, missing/inconsistent aggregate output references
are not repaired when all individual checkpoints remain valid. A prepare rerun
may already stale transcribe in some workflows, but the resume validator cannot
rely on that to satisfy its own current-input/output checks.
Reproduction: create complete alice/bob checkpoint records and aggregate
outputs with the existing fixture, mark the stage succeeded, remove bob from
the manifest's audio inputs, and call `ValidateResume`. It returns reusable
despite still retaining bob in the stored collection and aggregate outputs.
Required change:
- Distinguish no HTTP work from no reconciliation work. Validate that the
committed speaker set and aggregate transcript references represent the
reconciled current set, including relevant identity/evidence, before skipping.
- When only collection/output repair is needed, return non-resumable and let
normal execution checkpoint the pruned collection and rebuild aggregate
outputs without retranscribing reusable speakers. Preserve existing dependent
invalidation; do not force otherwise-current speaker jobs.
- Keep resume inspection read-only. Do not fix this by deleting incidental
transcript files or changing stage selection rules.
Verification: add removed-only input coverage (not just removal combined with
an added speaker), plus incomplete/inconsistent aggregate output coverage.
Exercise the runner skip path and assert zero additional WhisperX requests,
correct rebuilt outputs, and no removed speaker reaching merge.
## 2. Remove the Unrequested Positive Audio-Size Requirement
Priority: P2. Status: Pending.
Location: `internal/manifest/transcribe_state.go:95`.
The new record validator requires `AudioSize > 0`. Previously Narratio accepted
a regular `.flac` file and delegated transcription acceptance to WhisperX;
neither the user request nor the roadmap introduced minimum audio size or a new
audio-content validation policy. Zero-byte content still has a valid checksum
and can be represented by the checkpoint mechanism. The current check runs
after a successful response and output materialization, causing the entire
stage to fail instead of retaining the completed work.
Reproduction: prepare one speaker, replace its prepared audio with an empty
regular file, and use the existing successful fake WhisperX client. Stage
execution fails at checkpoint validation with `audio_size must be positive`.
This demonstrates the extra Narratio restriction; it does not assert that a
particular production WhisperX deployment accepts empty audio.
Required change:
- Permit zero audio size and reject negative sizes. Keep byte hashing and
size equality checks. Let the downstream service decide whether the input
can be transcribed, as before.
- Do not remove the nonempty valid-JSON output requirement, which has a separate
existing justification. Do not add a new early empty-audio rejection instead.
- Update any new tests/documentation that promote strictly positive input size
to a contract.
Verification: cover a zero-size record round-trip and successful adapter result
being checkpointed/reused for that input; retain negative-size rejection and
ordinary downstream-failure tests.
## Verification After Remediation
Run focused stage/manifest/application tests, changed-package race tests, and
the standard repository validation gates from `docs/development.md`. Preserve
the existing replay and cancellation regression tests; these findings do not
require redesigning the HTTP adapter.