One copy of every seam the outcome refactor will reshape - #37
Open
leeovery wants to merge 6 commits into
Open
Conversation
drivers/ytdlp.py now owns the probe, the audio download, the probe-failure vocabulary and the audio-cache scan; the youtube driver and the transcribe drain both consume it from there, and pipeline/transcribe.py stops importing a driver. Bug class made structurally impossible: the pipeline-imports-a-driver inversion — the standing driver-isolation violation (§2) through which a drain change could reach into one driver's module while every other driver stayed isolated, the same two-copies-drift class as the two admission paths that enforced the depth cap in only the dead one. With the seam in the lib layer there is no import path left from pipeline modules to any driver (registry.py, the Protocol-conformance point, is the one sanctioned exception), so the violation cannot be reintroduced by reaching for a helper that lives in a driver: the helpers no longer live there. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pipeline/enrichment.py now owns the format whole: the renderer and its YAML-1.1 quoting rule (from run.py), the fetched-stamp mask, and the two readers (from transcribe.py), parsing back through frontmatter.py's one scalar unquoting rule — render and parse as inverses in one place. lint, migration 2 and the run layer read through it; frontmatter.py's digest-side consumers are untouched. The readers' unterminated-fence contracts are preserved exactly: read_enrichment answers the shape with no fields, read_enrichment_fields raises on it, and the module states why the split is deliberate. The readers' contract tests move to tests/pipeline/test_enrichment.py with the code they pin. Bug class made structurally impossible: two frontmatter readers disagreeing on one file — the review record's truncated-fence class, where independently grown parsers answered the same interrupted write differently. Writer and readers now share one field grammar and one quoting rule, so a format change cannot land on one side of the round-trip without landing on the other. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The transcript-body composition (headings, youtube/podcast bodies, the pre-transcript split) moves into pipeline/enrichment.py: the youtube driver's _body/_description_section and the drain's copies were two spellings of one file-format contract, and the pre-transcript split already read the via frontmatter field to interpret the body — the sections and the frontmatter were never separable. URL_RE and slugify live once in pipeline/capture.py, imported by normalize; the two extension guesses become urls.ext_of with the per-family default (jpg media, mp3 audio) stated by the caller. Every pair verified line-identical before collapsing; the only divergence between the two ext helpers was the default, which is a real semantic distinction and stays as a parameter. Bug class made structurally impossible: a second copy of an output convention drifting until the two routes disagree — the review record's renamed-item incident, where a second spelling of the output-naming convention judged renamed items' media lost and requeued unfetchable work, and its heading twin, where an unstamped captions body was read as description end to end and a whisper drain stacked a second transcript under the first. The youtube driver can no longer write a body shape the drain does not split, because they call the same function; capture and normalize can no longer extract different URL sets or emit different id grammars from one note, because there is one pattern and one slug rule. Three pinning tests are added where the mutation spot-check found the shared helpers uncovered: ext_of's default fallback, and URL_RE's markdown-delimiter boundary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nine call sites hand-rolled the same guard around a transport call — except OSError to classify_connection, non-2xx to classify_http — across web (page fetch and wayback lookup), x, paper's feed, podcast, file, youtube's caption track, the transcribe drain's enclosure download and the media stage. fetch_classified now owns the pairing once, returning the response or a FetchFailure that carries the classification together with the wire status. The union is expressed, not averaged: seven callers route the classification onward exactly as before, and the two that deliberately never inherit classifier framing — the signed caption track whose 404 is transient, and the wayback lookup whose miss is only a note — read the plain wire fact off FetchFailure.detail, so every reason string and status is byte-identical to what each site produced. paper's full-text fallback keeps a bare transport call because it classifies nothing by design (a miss degrades to abstract-only). Bug class made structurally impossible: the N-copies-of-one-guard class behind the IncompleteRead incident, where http.client's protocol failures escaped every hand-rolled except-OSError guard at once and a truncated download landed as an engine bug instead of a retryable block — fixable then only by inventing a seam, because no single call site owned the pairing. A future change to what a fetch failure means lands in one function, and a new caller cannot re-derive the pairing subtly wrong, because the classification and the wire fact arrive together. A pinning test is added where the mutation spot-check found the caption-track variation uncovered: a 402 on a track URL keeps the plain wire spelling and blocked status, never the paywall framing a video-level 402 classifies to. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Classification.to_result() is now the one place a classified failure becomes a driver Result — exactly the classified status and its stated reason. Seven hand-rolled spellings collapse onto it: x, paper, youtube's probe arm, file's blob and download arms, and the _classified helpers in podcast and github; the wayback fallback appends its rescue note onto the classification's reason and converts through the same method, so the sanctioned append-context route no longer carries its own conversion. Bug class made structurally impossible: the copies-drift-apart class the review record shows with the two admission paths that enforced the depth cap in only the dead one. The driver-outcome change ahead replaces what a classification means as a result; with seven spellings that change lands at seven sites and history says one gets missed — with a method on Classification there is exactly one site, and a new driver cannot invent an eighth spelling that quietly drops the reason or re-decides the status, because the conversion is not assembled at call sites any more. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The seam-and-duplication cleanup cluster, behaviour-neutral and proven so: an independent review drove a 413-row matrix through this tip and the pre-wave baseline — byte-identical outputs, zero deltas.
Each commit names the bug class from this project's own review record that it makes structurally impossible:
drivers/fetch.py: NINE hand-rolled fetch-and-classify copies (the §16 count said eight) collapse onto one helper; the two deliberate wire-spelling flatteners keep their exact strings viaFetchFailure.detail— kills the N-copies-of-one-guard class behind the IncompleteRead incidentpipeline/enrichment.py: render and parse of the enrichment file format as inverses in one module, both unterminated-fence contracts preserved exactly — kills the two-readers-disagree classClassification.to_result(): SEVEN conversion spellings (count said six) collapse to one site — the outcome refactor's redefinition now lands in one placedrivers/ytdlp.py: the yt-dlp seam hoisted to the lib layer; no pipeline module imports a driver (registry's conformance list excepted)Mutation spot-checks on every new seam; three pre-existing coverage holes found and pinned along the way.
🤖 Generated with Claude Code