10 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | duration | completed | status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 14-rss-email-alert-ingestion-module-rollout | 01 | api |
|
|
|
|
|
|
|
|
|
13min | 2026-07-23 | complete |
Phase 14 Plan 01: Inbox Module Extraction Summary
Extracted DKV's IMAP/Exchange inbox providers into a shared apps/api/src/inbox/ module and added an additive fetchMessages() method returning subject + HTML/text body per unread message — the seam Plan 14-03's EmailAlertAdapter will consume.
Performance
- Duration: ~13 min
- Started: 2026-07-23T12:59:00+02:00 (approx.)
- Completed: 2026-07-23T13:09:45+02:00
- Tasks: 2/2 completed
- Files modified: 10 (5 new, 3 modified in Task 1; 2 new specs + 3 further modified in Task 2)
Accomplishments
- Moved
InboxProvider,ImapProvider,ExchangeInboxProvider, andInboxConfig/InboxAttachment/InboxEmailverbatim fromapps/api/src/dkv/providers/intoapps/api/src/inbox/, withdkv.types.tsre-exporting the moved types so no other DKV file's import needed to change - New
InboxModule(NestJS@Module) provides + exports both providers;DkvModulenow importsInboxModuleinstead of declaring the providers directly - Added
fetchMessages(config): Promise<InboxMessage[]>to the interface and both providers, entirely additive —fetchPdfAttachmentsbodies are byte-identical to before the move (verified viagit diff) - Net-new unit coverage: 13 tests across
imap.provider.spec.ts(7) andexchange-inbox.provider.spec.ts(6), covering body extraction, idempotent \Seen/IsRead marking, sender filtering, and graceful[]returns on connect/search/HTTP errors
Task Commits
Each task was committed atomically:
- Task 1: Move inbox providers into apps/api/src/inbox/ (behavior-preserving) -
eb668fd(refactor) - Task 2: Add fetchMessages() to InboxProvider + both implementations (net-new coverage) -
c404954(feat, TDD RED confirmed via 13 failing "not a function" assertions before implementation)
Plan metadata: (this commit, to follow)
Files Created/Modified
apps/api/src/inbox/inbox.module.ts- New NestJS module providing/exporting ImapProvider + ExchangeInboxProviderapps/api/src/inbox/inbox.types.ts- InboxConfig, InboxAttachment, InboxEmail (moved) + new InboxMessage typeapps/api/src/inbox/inbox-provider.interface.ts- InboxProvider interface, now with fetchMessagesapps/api/src/inbox/imap.provider.ts- ImapProvider, moved + fetchMessages/findBodyParts addedapps/api/src/inbox/exchange-inbox.provider.ts- ExchangeInboxProvider, moved + fetchMessages/getItemBodySoap/fetchMessagesViaEws addedapps/api/src/inbox/imap.provider.spec.ts- New: mocks ImapFlow, covers fetchMessagesapps/api/src/inbox/exchange-inbox.provider.spec.ts- New: mocks httpntlm.post via require.cache stub, covers fetchMessagesapps/api/src/dkv/dkv.service.ts- Import paths for ImapProvider/ExchangeInboxProvider changed to../inbox/...apps/api/src/dkv/dkv.module.ts- Imports InboxModule instead of declaring the two providers directlyapps/api/src/dkv/dkv.types.ts- Re-exports InboxConfig/InboxAttachment/InboxEmail from../inbox/inbox.types
Decisions Made
- Pure move, no logic changes (Task 1):
fetchPdfAttachments,testConnection, and all EWS SOAP builders were copied verbatim; only import paths changed. Verified viagit diff HEAD~1 HEADshowing zero edits inside the pre-existing method bodies. - Re-export shim in dkv.types.ts: rather than updating every DKV file that imports
InboxConfig/InboxAttachment/InboxEmail,dkv.types.tsnow re-exports them from../inbox/inbox.types, so only the two files that imported the providers (dkv.service.ts, dkv.module.ts) needed edits. - fetchMessages as a true sibling, not a refactor of fetchPdfAttachments: IMAP's
findBodyPartsmirrorscollectPdfParts's tree-walk shape but is a separate function; EWS'sgetItemBodySoapis a separate SOAP builder fromgetItemSoap. Neither touches the PDF-attachment call path — this was required by D-01/D-02 (DKV must stay behavior-identical) and verified by grepping the diff for edits insidefetchPdfAttachments/fetchViaEws. - Testing httpntlm's raw
require():vi.mock('httpntlm', ...)was verified (via a throwaway repro test) to have zero effect onexchange-inbox.provider.ts'sconst httpntlm = require('httpntlm')— Vitest's mock interception only covers the ESM module graph, and a literalrequire()call resolves through Node's real module cache regardless. The spec instead pre-seedsrequire.cache[require.resolve('httpntlm')]with a stub{ post }in abeforeAll(dynamicimport()of the provider, deferred out of module scope so the file stays compatible with the project'scommonjstsconfig which rejects top-levelawait/import.meta).
Deviations from Plan
None — plan executed exactly as written. The require.cache testing approach for exchange-inbox.provider.spec.ts is a test-infrastructure detail within Task 2's stated scope ("mirroring cosinex.adapter.spec.ts's mock style ... stub httpntlm.post") — the plan did not prescribe how to intercept a raw CJS require(), and the chosen approach achieves the same outcome (httpntlm.post fully mocked, zero real network calls) without touching the provider's production code (which the plan explicitly forbids "improving" or refactoring).
Issues Encountered
- Discovered mid-Task-2 that
vi.mock('httpntlm', ...)does not interceptexchange-inbox.provider.ts's rawrequire('httpntlm')call (confirmed via a minimal repro:vi.isMockFunction(require('httpntlm').post)returnedfalse, and all 6 initial test attempts hit a real DNS lookup —getaddrinfo ENOTFOUND mail.example.com— with 3 "passing" only by coincidence since their assertions expected[], which a failed network call also produces). Resolved via therequire.cachestub approach described above; re-verified all 6 exchange-inbox tests genuinely exercise the mockedhttpntlm.post(assertions onmessages[0]content, not just[]).
User Setup Required
None - no external service configuration required.
Next Phase Readiness
apps/api/src/inbox/is ready for Plan 14-03'sEmailAlertAdapterto injectImapProvider/ExchangeInboxProvider(viaInboxModule) and callfetchMessages(config)for the tender-alert mailbox — with its own independent mailbox config (D-03), not DKV's.- DKV's regression surface (fetchPdfAttachments, the full pipeline) is unchanged and the full API suite (301 tests) is green; no DKV-specific spec file exists yet, so the regression guard for this plan was the compile (
tsc --noEmit) plus the rest of the suite not regressing — a gap the plan itself flagged as pre-existing (nodkv/*.spec.tsfiles in the repo).
Phase: 14-rss-email-alert-ingestion-module-rollout Completed: 2026-07-23
Self-Check: PASSED
All 7 created/modified inbox files verified on disk; both task commits (eb668fd, c404954) verified in git log --oneline --all.