diff --git a/backend/app/services/ingest_core.py b/backend/app/services/ingest_core.py index d8ee3b7..072a19c 100644 --- a/backend/app/services/ingest_core.py +++ b/backend/app/services/ingest_core.py @@ -141,9 +141,18 @@ class Ingester: # tick has no resumable backfill state. checkpoint = mode in ("backfill", "recovery", "recapture") ledger_key = self._ledger_key - # Optional seams (Patreon native ingester): capture pure-text posts that - # have NO downloadable media so the artist archive is complete. Absent on - # stub clients/downloaders (unit tests) → media-less posts skipped as before. + # POST-FIRST CONTRACT (milestone #67): these two optional seams make a + # platform "post-first" on the native core ingester — the post-record is + # the single authoritative writer of the post body/links/metadata, and the + # per-media sidecar carries image identity only (download_service flips + # importer.post_first via uses_native_ingester, so the import side follows + # automatically). A platform migrating off gallery-dl onto the native core + # adopts post-first by implementing BOTH: + # client.post_record_key(post) -> (ledger_key, post_id) | None (gate) + # downloader.write_post_record(post, artist_slug) -> PostRecordOutcome + # Absent on stub clients/downloaders (unit tests) and on not-yet-migrated + # platforms → media-less posts are skipped as before and the body still + # comes from the per-media sidecar (gallery-dl path). See [[post-first-ingest-contract]]. post_record_key = getattr(self.client, "post_record_key", None) write_post_record = getattr(self.downloader, "write_post_record", None) start = time.monotonic()