Skip to content

One copy of every seam the outcome refactor will reshape - #37

Open
leeovery wants to merge 6 commits into
fix3/e16-installfrom
fix3/e17-seams
Open

One copy of every seam the outcome refactor will reshape#37
leeovery wants to merge 6 commits into
fix3/e16-installfrom
fix3/e17-seams

Conversation

@leeovery

Copy link
Copy Markdown
Owner

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 via FetchFailure.detail — kills the N-copies-of-one-guard class behind the IncompleteRead incident
  • pipeline/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 class
  • Body/slug/extension helpers deduplicated (driver and drain now compose transcripts through one function; capture and normalize share one URL pattern and one id grammar) — kills the second-copy-drift class behind the renamed-item media incident
  • Classification.to_result(): SEVEN conversion spellings (count said six) collapse to one site — the outcome refactor's redefinition now lands in one place
  • drivers/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

leeovery and others added 6 commits August 24, 2026 11:06
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>
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