Skip to content

Extract ChatDBReader and MessageBuilder from DatabaseHandler - #76

Merged
Skeyelab merged 1 commit into
masterfrom
Skeyelab/gh-issue-67-chatdb-split
Jun 17, 2026
Merged

Skeyelab merged 1 commit into
masterfrom
Skeyelab/gh-issue-67-chatdb-split

Conversation

@Skeyelab

Copy link
Copy Markdown
Member

Closes #67

Summary

Deepens DatabaseHandler by splitting its chat.db→Message responsibilities into two focused adapters:

  • ChatDBReader — owns the read-only SQLite handle and all SQL. Returns plain row DTOs (MessageRow, GroupParticipantRow, AttachmentRow). Resolves message text from either the text column or the Ventura-era attributedBody blob. No Message, routing, or contact knowledge.
  • MessageBuilder — converts DTOs → Message/Person/Group/Attachment. Owns isFromMe sender/recipient branching, mailto: stripping, and group/attachment assembly. contactNameResolver is injected, so it is unit-testable without the Contacts XPC service or a live database.
  • DatabaseHandler — now polling/WAL orchestration plus the inbound deferral policy (deferring rows once while Messages backfills handle_id) only.

MessageSource seam removed

Per the acceptance criterion to evaluate whether the MessageSource seam earns its keep: it was a pass-through (onMessage: ((Message) -> Void)?) whose only consumer was an unused DatabaseHandler convenience init. With ChatDBReader+MessageBuilder now the real seam, MessageSource, InMemoryMessageSource, the convenience init, and MessageSourceTests are removed.

Test plan

  • bash ./.github/scripts/test-macos.sh — 114 tests, all pass
  • Existing DatabaseHandlerTest (poll/defer/self-chat/outgoing/group edge cases) passes unchanged against the refactor
  • New MessageBuilderTests (11 tests) cover DTO→entity conversion in isolation: incoming/outgoing sender resolution, mailto: stripping, group recipient, contact-name resolver, group GUID fallback, attachment mapping

Acceptance criteria

  • ChatDBReader has no Message / routing knowledge
  • MessageBuilder tested in isolation (no SQLite/Contacts)
  • DatabaseHandlerTest still passes
  • Evaluated MessageSource seam → removed (did not earn its keep)

🤖 Generated with Claude Code

ChatDBReader owns the SQLite read layer (SQL → row DTOs, attributedBody
decoding); MessageBuilder converts DTOs → Message/Person/Group/Attachment
with an injectable contactNameResolver. DatabaseHandler keeps polling/WAL
orchestration and inbound deferral policy only.

Removes the unused MessageSource pass-through seam (protocol +
InMemoryMessageSource + convenience init) now that the real chat.db→Message
seam has dedicated adapters.

Adds MessageBuilderTests covering DTO→entity conversion in isolation
(no SQLite or Contacts dependency).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Skeyelab
Skeyelab merged commit 7090011 into master Jun 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Architecture: extract ChatDBReader and MessageBuilder from DatabaseHandler

2 participants