Skip to content

[Audit][Follow-up] AP 인바운드 파싱 경로의 pre-existing 결함 정리 (PR #870에서 분리) #871

Description

@stadia

배경

PR #870 ([Audit][MODEL-01]) 는 Post/Article 코어에서 인바운드 ActivityPub 파싱과 마크다운 렌더링을 각각 Posts::FederationIngest 개념(concern)과 Articles::Markdown 함수 모듈로 동작 보존(byte-equivalent) 리팩터링으로 추출했다.

멀티 에이전트 리뷰 결과, 아래 항목들은 이번 PR이 만든 회귀가 아니라 추출로 인해 격리되어 이제 고치기 쉬워진 pre-existing 결함이다. byte-equivalence 보증을 깨지 않기 위해 PR에서 분리하여 후속 이슈로 정리한다.

관련: PR #870 리뷰 요약, 신규 직접 테스트(test/functions/articles/markdown_test.rb, test/models/concerns/posts/federation_ingest_test.rb)가 현재 동작을 핀 고정해 둠.


🟡 정리 대상

1. article_id 값 타입 불일치 (String vs Integer)

app/models/concerns/posts/federation_ingest.rb:58-63

/articles/(\d+) 분기는 정규식 캡처 결과인 String 을, 페더레이션 부모 분기는 .pick / parent.article_idInteger/nil 을 반환한다. 쿼리에서 Rails가 강제 변환해 지금은 동작하지만 Hash[Symbol, untyped] 시그니처가 이를 숨긴다.
→ 추출 시점에 Integer로 정규화(article_id.to_i)하고 시그니처를 좁힌다.

2. /posts/(\d+) 로컬 분기의 dangling parent_id

federation_ingest.rb:60-61

페더레이션 부모 분기(find_by 로 가드)와 달리, 로컬 numeric 분기는 parent_id: post_id 를 무조건 설정한 뒤 article_id 를 별도 조회한다. 매칭과 조회 사이에 로컬 post가 삭제되면 parent_id 가 없는 행을 가리켜 저장 시 FK 위반이 발생한다.
Post.find_by(id: post_id) 로 가드.

3. Orphan / 예외 경로가 조용히 삼켜짐 (로깅 부재/부족)

silent-failure 지적:

  • federation_ingest.rb:66 — 해소 실패한 inReplyTologger.debug 로만 남긴다. 프로덕션에서 debug는 꺼져 있어 orphan이 top-level post로 조용히 재분류된다. → warn/info 로 승격.
  • federation_ingest.rb:88URI::InvalidURIError rescue가 로깅 없이 삼킨다.
  • federation_ingest.rb:20hash[\"id\"] 가 nil인 경우 로깅 없이 통과.

🟢 폴리시/문서 개선

4. display_summary_key String-vs-Array 잠재 버그

app/functions/articles/markdown.rb:31

.map 이 Array를 가정한다. display_summary_key 가 비어있지 않은 String을 반환하면 .map문자 단위로 순회한다(형제 summary_key_preview 는 양쪽을 처리함).
LocalizedDisplay#display_summary_keyArray? 로 좁히거나 Array(key).flat_map 사용.

5. local_reply_target? 가 bool 술어인데 로깅 부작용을 가짐

federation_ingest.rb:86:local | :remote | :misconfigured 같은 타입 있는 enum 반환으로 부작용을 타입에 드러내는 것을 검토.

6. body 우선순위 체인 문서화

federation_ingest.rb:92-104contentMap → content → summary → 첨부 이름 결합 → I18n sentinel 순서가 파일에서 가장 비직관적인 로직인데 주석이 없다.


❌ 리뷰 지적 중 제외 (검증 결과 결함 아님)

리뷰의 "discard된 부모가 답글을 조용히 orphan화한다" 는 전제는 사실이 아님을 런타임으로 확인했다. Post 에는 kept default_scope가 없어(Post.default_scopes == []) Post.find_by/exists? 가 soft-deleted 행을 그대로 반환한다. 따라서 discard된 부모에 대한 답글은 정상적으로 parent_id/article_id 로 해소된다. 현재 동작은 federation_ingest_test.rb 에 핀 고정됨. (별도 조치 불필요)


🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions