Harden site_generator page generation against malformed data (#74) - #76
Merged
Merged
Conversation
generate_activities_pages ran par_iter().for_each(|d| ...unwrap()), and generate_activity_page unwrapped both the activity's actor id and the actor lookup — so a malformed/incomplete import (a public activity whose actor is missing or un-parseable) panicked mid-build. - generate_activities_pages now uses par_iter().try_for_each(...) so a genuine render/IO error propagates as Err instead of panicking a rayon worker. - generate_activity_page filters public activities first, then skips (with a warn!) any whose actor id is absent or whose actor isn't in the map, mirroring the resilience of db::actors::get_actors. A missing actor degrades one activity, not the whole build. Add a regression test: a day with an activity whose actor is absent renders without panic (the activity is skipped). Closes #74 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Hardens the site generator’s per-day page rendering so malformed activity data (specifically unresolved actors) no longer panics mid-build, aligning runtime behavior with the existing “skip bad rows” approach already used in actor loading.
Changes:
- Switch per-day parallel generation from
for_each(...).unwrap()totry_for_each(...)so render/IO failures propagate asResultinstead of panicking rayon workers. - In
generate_activity_page, replace actor-resolutionunwrap()s with skip-with-warn!when an activity’s actor id is missing or the actor is absent from the provided map. - Add a regression test proving orphaned-actor activities are skipped without aborting page generation.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/site_generator.rs |
Removes panic paths in parallel generation and actor resolution; adds regression test for missing-actor resilience. |
docs/dev-sessions/2026-07-24-1732-harden-site-generation/spec.md |
Documents the robustness issue and desired behavior for #74. |
docs/dev-sessions/2026-07-24-1732-harden-site-generation/plan.md |
Captures the implementation plan and verification steps for the fix. |
docs/dev-sessions/2026-07-24-1732-harden-site-generation/notes.md |
Empty session notes placeholder for the work. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+147
to
+151
| let items: Vec<activitystreams::Activity> = db_activities | ||
| .get_activities_for_day(day)? | ||
| .iter() | ||
| .map(|activity| { | ||
| let actor_id: &String = activity.actor.id().unwrap(); | ||
| let actor: &activitystreams::Actor = actors.get(actor_id).unwrap(); | ||
| (activity, actor) | ||
| }) | ||
| .filter(|(activity, _actor)| { | ||
| // todo: any actor-related filtering needed here? | ||
| activity.is_public() | ||
| }) | ||
| .map(|(activity, actor)| { | ||
| .filter(|activity| activity.is_public()) | ||
| .filter_map(|activity| { |
…_page get_activities_for_day returns an owned Vec<Activity>, so iterate by value (into_iter) and mutate the owned activity in place instead of .iter() + clone, dropping one allocation/copy per rendered activity. Co-Authored-By: Claude Opus 4.8 (1M context) <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.
Closes #74 (follow-up from #73's Copilot review).
Problem
generate_activities_pagesranpar_iter().for_each(|d| generate_activity_page(...).unwrap()), andgenerate_activity_pageunwrapped both the activity's actor id and the actor-map lookup. So a malformed or incomplete import — a public activity whose actor is missing or un-parseable — panicked mid-build instead of surfacing an error. Theactorsmap comes fromget_actors_by_id, which already skips un-parseable actor rows, so the DB can legitimately lack an actor an activity references.Fix
generate_activities_pagesusespar_iter().try_for_each(...), so a genuine render/IO error propagates asErrinstead of panicking a rayon worker.generate_activity_pagefilters public activities first, then skips (with awarn!) any whose actor id is absent or whose actor isn't in the map — mirroring the resilience already indb::actors::get_actors. A missing actor degrades one activity, not the whole build..unwrap()remains on the actor-resolution path.Verification
Regression test
generate_activities_pages_skips_activity_with_missing_actor: a day containing an activity whose actor is absent renders without panic (activity skipped). Confirmed red before / green after the fix.make check+make testgreen (44 lib tests).Behavior-preserving for well-formed data.
Spec/plan:
docs/dev-sessions/2026-07-24-1732-harden-site-generation/.🤖 Generated with Claude Code