feat(blog): prune posts removed or renamed upstream

the blog repo is the source of truth for the full set of posts, but
upserts alone never delete: removing a file or changing a slug or
filename left the old row serving forever. each poll now reconciles —
after upserting the current tree, events under source='blog' whose id
is not in the parsed set are deleted via a new EventWriter::prune_events
port. nothing is lost: git still has every post, and restoring or
fixing a file re-ingests it on the next tip change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 22:56:54 +03:00
parent cd3dc2d82d
commit 37c44906bb
4 changed files with 33 additions and 1 deletions

View File

@@ -171,6 +171,18 @@ impl EventSource for BlogSource {
}
let total = self.writer.upsert_events(&events).await?;
// The repo is the source of truth for the full set of posts: anything
// stored under source='blog' that no longer parses out of the current
// tree (deleted file, renamed slug/filename, broken frontmatter) is
// pruned. Nothing is lost — git still has it, and fixing or restoring
// the file re-ingests it on the next tip change.
let keep: Vec<String> = events.iter().map(|e| e.id.clone()).collect();
let pruned = self.writer.prune_events(Source::Blog, &keep).await?;
if pruned > 0 {
tracing::info!(pruned, "blog posts removed upstream; pruned from store");
}
self.state.save(SOURCE_NAME, Some(&tip), None).await?;
debug!(ingested = total, tip = %tip, "blog poll complete");
Ok(total)

View File

@@ -479,6 +479,22 @@ impl EventWriter for PgStore {
Ok(inserted)
}
async fn prune_events(&self, source: Source, keep_ids: &[String]) -> Result<usize, StoreError> {
let n = sqlx::query(
r#"
DELETE FROM events
WHERE source = $1 AND NOT (id = ANY($2))
"#,
)
.bind(source.as_str())
.bind(keep_ids)
.execute(&self.pool)
.await
.map_err(map_err)?
.rows_affected();
Ok(n as usize)
}
async fn upsert_repo_languages(&self, languages: &[RepoLanguage]) -> Result<usize, StoreError> {
if languages.is_empty() {
return Ok(0);