Files
jobtrackingapp/docs/verification/mail-001-job-email-hub.md
T
cesnimda 429f427f49
CI and Deploy / test (pull_request) Failing after 1m34s
CI and Deploy / deploy (pull_request) Has been skipped
docs(email): record send ledger evidence
2026-08-09 23:41:09 +02:00

5.9 KiB

MAIL-001 consolidated job-email hub

Updated: 2026-08-09

Status: IN PROGRESS. Canonical hub routing and provider-neutral read capability discovery are implemented and locally verified; provider actions and draft/send work remain.

Revalidated current boundaries

  • /correspondence listed persisted, job-linked Correspondence rows with search/direction/link filters.
  • /correspondence/review separately rendered Gmail review candidates/suggested jobs and linked back to the inbox.
  • The job workspace already embeds the shared Correspondence component and therefore reads/writes the same underlying rows rather than a copy.
  • Gmail review decisions, import/link/unlink/relink and job creation use existing Gmail APIs. Outlook/Graph and IMAP connection models exist, but this review surface is Gmail-specific.
  • The per-job composer currently logs a message to Correspondence; it is not a provider-send draft flow. Separate follow-up sending exists elsewhere and must not be mislabeled or silently reused.

Implemented first increment

  • /correspondence is the canonical Job email hub with linked-message and recruitment-suggestion views represented by ?view=review.
  • The review component embeds under the hub with correct heading hierarchy and without duplicate back navigation.
  • /correspondence/review remains a compatibility route and redirects to the canonical filtered hub.
  • Switching to review does not issue the linked-correspondence query; switching views reuses the existing tested review component and APIs.
  • No provider connection, sync, import, link or send behavior changed.

Implemented provider-neutral read increment

  • Added one authenticated /api/email controller over the existing IEmailProviderRegistry for provider status, search, thread summaries and plain-text message detail.
  • Every operation passes the authenticated owner ID into the registered Gmail, Outlook or IMAP adapter and rejects unknown or disconnected providers before mailbox access.
  • Message detail intentionally omits provider HTML. Untrusted provider markup is not exposed through this shared endpoint.
  • The hub now identifies connected and disconnected providers and advertises their actual capability. Gmail, Outlook and IMAP are currently shown as read-only because their installed scopes/contracts do not implement provider send.
  • The controller does not change OAuth scopes, connect accounts, invoke providers in tests or claim that the legacy SMTP follow-up sender is provider-native.

Implemented safe message-detail increment

  • Provider-backed rows now open plain-text detail through /api/email/message; the UI never renders provider HTML.
  • If a provider is disconnected or unavailable, the hub clearly warns and shows the owner-scoped saved JobTracker copy instead of losing access to imported correspondence.
  • Manual/internal rows use the same detail shape through /api/correspondence/message/{id}.
  • Direct saved-message IDs remain tenant-filtered. Malformed legacy label/attachment JSON degrades to empty metadata instead of breaking the message view.
  • Rapid selection changes invalidate older requests so late provider responses cannot appear under the wrong message.
  • Inbox label and attachment counts now reflect parsed metadata arrays rather than treating every non-null JSON field as one item.

Implemented inert send-ledger increment

  • Added tenant-owned EmailSendAttempts with pending/sending/sent/failed/uncertain states and a unique owner/client-request key.
  • A request ID can be reused only for the same SHA-256 payload hash. Different content under an old request ID is rejected.
  • Only pending attempts may enter sending, and only sending attempts may become terminal. Failed or uncertain attempts cannot be restarted blindly.
  • The ledger stores provider/idempotency/status/timing metadata only; recipient, subject and message body are intentionally absent.
  • Deleting the owning job cascades the ledger row. The global owner filter protects direct attempt IDs.
  • The additive migration has provider-specific SQLite/MariaDB types and reversible up/down SQL. No send route, OAuth scope or provider call was enabled.

Verification

  • Focused send ledger: 3/3; provider/correspondence controllers: 5/5; hub detail: 5/5.
  • Full backend: 608/608; full frontend: 49/49 suites, 188/188 tests.
  • Production build/TypeScript and git diff --check: pass.
  • Implementation commits: 6008b4a, 536d403, a20775c, 653f011.

Remaining MAIL-001 work

  • Extend shared provider-neutral thread navigation and application embedding while preserving provider capability differences.
  • Surface provider identity, reauthorization, read/unread, pin/read-later/archive/spam/trash only where the provider supports it.
  • Share thread detail and link/unlink actions between hub and job workspace.
  • Design editable provider drafts with recipient/subject/thread/provider review and explicit confirmed, idempotent send; uncertain failures must not be retried blindly.
  • Include non-sensitive send-attempt metadata in user export and verify account/job deletion coverage before enabling the send API.
  • Preserve minimal audit metadata without sensitive body logging; verify Free non-AI access and Pro-only AI assistance.
  • Complete link/unlink/dismiss/draft/send/failure/two-user/application-embed tests plus browser/production provider gates. No real email may be sent during repository verification.

Validation limitation

The first focused Jest invocation exhibited the repository's open-handle delay. The passing focused and full runs used --forceExit; the full run took 228.709 seconds. A Next build process also failed to exit after compilation; only the exact PIDs started by those build attempts were stopped, then a clean build completed. This is recorded as tooling/runtime behavior, not hidden.

Rollback

Revert 653f011 (after downgrading the additive migration) for the send ledger, a20775c for message detail, 536d403 for neutral reads, then 6008b4a for routing. Provider scopes remain unchanged.