82 lines
7.1 KiB
Markdown
82 lines
7.1 KiB
Markdown
# 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.
|
|
|
|
## Implemented provider-delivery adapter increment
|
|
|
|
- Gmail and Microsoft Graph authorization URLs now request explicit send consent in addition to read access. Existing read-only connections remain read-only until the user reconnects.
|
|
- Provider status derives send capability from the stored granted scope. IMAP remains read-only because it has no configured outgoing transport.
|
|
- Gmail builds an RFC MIME plain-text message, supports the existing Gmail thread ID, and uses the documented send endpoint. Graph sends plain-text JSON through `sendMail`.
|
|
- HTTP rejection is a known failed-before-delivery category; 401/403 requires reauthorization. Network interruption/cancellation is marked uncertain because acceptance cannot be disproved.
|
|
- Provider response bodies and transport exception details are not returned to callers. Recipient/body fixtures and HTTP transport are synthetic/mocked; no provider was contacted.
|
|
- No application send route exists yet. Consent and adapters cannot be triggered by a JobTracker send button until ledger integration lands.
|
|
|
|
## Verification
|
|
|
|
- Focused delivery/provider/capability: 18/18; send ledger: 3/3; provider/correspondence controllers: 5/5; hub detail: 5/5.
|
|
- Full backend: 613/613; full frontend: 49/49 suites, 188/188 tests.
|
|
- Production build/TypeScript and `git diff --check`: pass.
|
|
- Implementation commits: `6008b4a`, `536d403`, `a20775c`, `653f011`, `e9937ac`.
|
|
|
|
## 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 `e9937ac` to remove send consent/adapters, then `653f011` (after migration downgrade) for the ledger, followed by the earlier read/routing commits. Existing provider grants are not revoked by a code rollback; disconnect/reconnect is an explicit user action.
|