fix(materialize): bind posts and comments to the community that delivered them master
The ingest fix in 199ae81 made announced deliveries the only way content is materialized, but the materializer still took a post's community from the object's own audience and a comment's from its parent chain, which the announcing server fetches and controls. Community C could announce a reply whose parent chain led into community D's thread and it was stored in D. A same-host origin could serve a backfilled post with audience D while C's outbox was being walked, and it was accepted into D. Every materializer entry point now takes the community that delivered the content, and every stored post and comment is bound to that community. Changes: - MaterializePost, MaterializeComment and HandleUpdate take the bound community IRI; an empty one is a validation error. The inbox passes the announcer, backfill the walked community, an announced restore the announcer, and a bare restore of content is not applied. - requireBoundCommunity compares the content's stored or derived community DID with the bound community's. A missing communities row, an empty bound DID or a binding that cannot be derived is refused with its own skip reason and a Warn log. - Comments: the leaf and every unmapped ancestor are drafted before the walk commits anything, so a refused ancestor leaves nothing behind. Ancestor aliases (fetched id differs from the requested one), types other than Note or Page, and soft-deleted stored ancestors are refused. - commitRecord binds the commit to the delivery's community: a content mapping already bound to another community is a skip at the read, and a concurrent binding inside the transaction rolls it back (errCommunityBindingRaced) and becomes a skip. - Deleting a legacy mapping with an empty community_did first derives the binding from the record and persists it (SetCommunityDIDIfUnset), so a later restore can still be bound. Failures are logged and never block the delete. - Backfill passes the walked community for posts and replies; its count-seeding comparison stays as defense in depth. - Tests: community_binding_test.go covers cross-community parents, alias and Page-root escapes, refused leaves and middle ancestors, deleted redeliveries, legacy restores and first-materialization races; ingest wiring tests cover announce, backfill and restore. Closes 2026-10-03-tidepool-announced-comment-parent-not-community-bound and 2026-10-03-tidepool-backfill-not-community-bound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>