Milestone #67 step 2. On the native core ingester the Post becomes the single
authoritative record for body/links/metadata, captured once per post by the
post-record; the per-media import only links image provenance + localization.
Before: every per-media sidecar carried the full post body, so a post with N
images wrote the body N+1 times (post-record + N media) — redundant on disk and
a divergence risk (#753). gallery-dl is unchanged (its sidecar is still the only
body source).
- patreon_downloader: the per-media sidecar is now minimal — {category, id,
source_url} only, no body. `_write_sidecar_data(minimal=True)` skips the body
resolution + detail-fetch (the post-record, written first in the walk, already
did it). Body no longer duplicated next to each image.
- importer: new per-instance `post_first` flag (Importer is per-task). When set,
`_apply_sidecar` still writes source_filehash + provenance + primary_post_id
but SKIPS `_apply_post_fields` (the post-record owns body/links/raw_metadata,
so applying a body-less sidecar would clobber raw_metadata + re-sync links off
empty data). Default False keeps gallery-dl writing post fields.
- download_service: `_phase3_persist` sets importer.post_first =
uses_native_ingester(platform) — the future-proof seam, so a platform migrating
onto the native core flips to post-first automatically (step 3). Media imports
before post-records but both unify on external_post_id, so the post ends with
its body either way.
Tests: per-media sidecar is minimal + never hits the detail fetcher; attach
post_first=True links provenance/localization but writes no post body/title;
post_first=False (gallery-dl) still applies them.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>