AutoResponder Service Status Tracker#
Last verified against code: see repository
Snapshot template: When sharing a run-specific status, set: WRITE_OPERATIONS_ENABLED=…, MOCK_ALL_SERVICES_FOR_DEV=…, DRY_RUN_USE_REAL_EMAIL_REPOSITORY=…, EMAIL_PULL_START_DATE=…, EMAIL_PULL_END_DATE=…, CUPOLA_AUTOMATIC_UPDATES=….
This document summarizes how each integration behaves by operating mode. It is not a substitute for connections/README.html, README.html (documentation index), or main.py; refresh it when wiring changes.
Active-only policy. The action engine now gates every automated write on an active CUPOLA row. ACTIVE determinations never auto-add or auto-reactivate; EMAIL_UPDATE / TITLE_UPDATE only fire when the preferred CUPOLA row exists and is active; OUT_OF_OFFICE is a first-class determination that writes nothing to any backend. The per-connector "LIVE" columns below describe what happens when the gate passes; otherwise the row is routed to the Human Review digest (see docs/OutputDocument.html).
Operating Modes#
| Mode | Email repo | DB connectors | LLM | SFMC | Notifier |
|---|
Live (WRITE_OPERATIONS_ENABLED=true, MOCK_ALL_SERVICES_FOR_DEV=false) | Live MSSQL | Live (with mock fallback where configured) | Live | Live (mock fallback if unhealthy) | Live |
Simulation (no writes) (WRITE_OPERATIONS_ENABLED=false) | Live MSSQL | Live reads; writes intercepted via ReadOnlyDatabaseWrapper where wrapped | Live | Mock client | Dry-run (logged, not sent) |
Dev mock-all (MOCK_ALL_SERVICES_FOR_DEV=true) | Mock by default; optional live inbox if dry_run_use_real_email_repository=true | Cupola / Multipub / Salesforce: mocks. Hodor: real HodorDatabase (graceful-unavailable if unset). SFMC: mock. | Live | Mock | Dry-run |
Read-only: Cupola, Multipub, Salesforce, and Hodor (when SQL is configured and available) use ReadOnlyDatabaseWrapper for writes. SFMC uses a mock client.
Hodor business behavior: With Hodor SQL configured, lookups still hit the database; ActionEngine emits Prospect Import rows and sends them via notify_hodor_prospect_import to NOTIFICATION_EMAIL_VENU (catalog N01). Details: connections/hodor.html.
Service-by-Service Status#
1. Email Repository#
| Operation | Status | Notes |
|---|
get_emails() | Live in non–dry-run and in dry-run when dry_run_use_real_email_repository=true | Reads CBI_EmailExtraction / emails (see connections/email-repository.html) |
| Mock in dry-run when dry_run_use_real_email_repository=false (default) | Fixture data from MockEmailRepository |
- File:
src/auto_responder/services/email_repository.py - Config:
email_repository_connection_string, dry_run_use_real_email_repository
2. LLM Classification (Determination Service)#
| Operation | Status | Notes |
|---|
classify_email() | LIVE (always) | Real OpenAI/Ollama calls in all modes |
qa_check() | LIVE (always) | |
validate_email() | LIVE (always) | |
identify_auto_response_source_email() | LIVE (always) | |
map_category_to_determination() | LIVE (always) | Pure mapping logic, no external calls |
- Files:
src/auto_responder/services/determination/llm_classifier.py, autoresponsedetermination.py, category_mapper.py - Config:
openai_api_key, classifier_backend, ollama_model, ollama_url
3. Cupola Database#
| Operation | Read-only | Live Mode | Dry-Run | Notes |
|---|
lookup_by_email() | LIVE read | LIVE | Mocked | |
update_contact_status() | Intercepted | LIVE only when _handle_inactive passes the active-with-org gate and CUPOLA_AUTOMATIC_UPDATES=true. Never called for reactivation; inactive CUPOLA rows on ACTIVE determinations route to Human Review. | Mocked | Wrapper around update_contact_status_with_audit. |
update_contact_status_with_audit() | Intercepted (returns (True, None) — previous status cannot be observed) | LIVE when invoked by _handle_inactive. Uses OUTPUT deleted.status to capture previous_status atomically, which CupolaAuditLogger records and uses to write cupola_audit_log_rollback_plan.csv. | Mocked (returns (True, 1)) | The audited entry point used by the action engine. |
check_active_subscription() | LIVE read | LIVE | Mocked | |
add_contact() | Intercepted | LIVE only from REPLACEMENT flow (cupola_auto_add_replacements=true). Never called from _handle_active. | Mocked | See CUPOLA_UPDATE_STATEMENTS.html §5. |
update_contact_email() | Intercepted | LIVE only when _handle_title_update passes the active-only CUPOLA gate. | Mocked | |
update_contact_title() | Intercepted | LIVE only when _handle_title_update passes the active-only CUPOLA gate. | Mocked | |
| Other reads | LIVE | LIVE | Mocked | |
- File:
src/auto_responder/services/databases/cupola.py - Config:
cupola_connection_string or cupolaConfig JSON; CUPOLA_AUTOMATIC_UPDATES (default false) controls whether the action engine actually writes vs. only emits Venu recommendations. - Fallback:
MockCupolaDatabase if connectivity check fails - Audit: Every action is recorded by
CupolaAuditLogger (utils/cupola_audit_logger.py); auto-applied changes get an additional cupola_audit_log_rollback_plan.csv with revert SQL.
4. Multipub Database#
| Operation | Read-only | Live Mode | Dry-Run | Notes |
|---|
| Reads | LIVE | LIVE | Mocked | |
Writes (update_*, add_contact, etc.) | Intercepted | LIVE | Mocked | |
- File:
src/auto_responder/services/databases/multipub.py - Config:
multipub_order_server, multipub_order_database, multipub_order_user, multipub_order_password - Fallback:
MockMultipubDatabase if connectivity check fails
5. Hodor Database (SQL Server)#
Direct pyodbc access to the prospect table (see HODOR_DATABASE_SCHEMA.html). Not browser automation.
| Operation | Read-only | Live Mode | Dry-Run | Notes |
|---|
lookup_by_email() / lookup_batch() | LIVE read when configured | LIVE | Real connector; contact lookup step is skipped in dry-run (see §10), but HodorDatabase is still constructed | Graceful-unavailable if HODOR_* / HODOR_CONNECTION_STRING unset |
update_contact_status(), update_contact_email(), update_contact_title() | Intercepted when wrapped (available) | Connector methods exist, but business handlers emit Prospect Import rows instead of invoking these writes | Not wrapped (dry-run uses mocks for other DBs; Hodor is real class — see main.py) | |
add_contact() | Logged / no-op per connector | | | Not fully implemented |
check_active_subscription() | LIVE read when implemented | LIVE | | |
- File:
src/auto_responder/services/databases/hodor.py - Config:
HODOR_DB_HOST, HODOR_DB_DATABASE, HODOR_DB_USER, HODOR_DB_PASSWORD, HODOR_DB_PORT, or HODOR_CONNECTION_STRING; column overrides in config.py; NOTIFICATION_EMAIL_VENU for N01 Prospect Import digest delivery. - Read-only: When
hodor_real.available, main.py wraps with ReadOnlyDatabaseWrapper — writes are logged, not executed. - Hodor routing mode: The connector may still run SELECTs; business handlers route status/email/title updates to Prospect Import rows plus notification (§8–9).
- Fallback: Graceful-unavailable (no mock class): lookups return empty if not configured.
6. Salesforce#
| Operation | Read-only | Live Mode | Dry-Run | Notes |
|---|
lookup_by_email() | LIVE read (or mock fallback) | LIVE | Mocked | |
| Writes | Intercepted (if real connector) | LIVE | Mocked | |
- File:
src/auto_responder/services/databases/salesforce.py - Config:
salesforce_access_token + salesforce_domain (preferred), or username/password/security token - Fallback:
MockSalesforceDatabase if initialization or connectivity check fails
7. SFMC (Salesforce Marketing Cloud)#
| Operation | Read-only | Live Mode | Dry-Run | Notes |
|---|
add_to_suppression_list() | Mocked | LIVE (mock fallback) | Mocked | |
check_health() | Mocked | LIVE | Mocked | |
- Files:
src/auto_responder/services/sfmc/client.py (live), src/auto_responder/services/sfmc/mock.py (mock) - Config:
sfmc_client_id, sfmc_client_secret, sfmc_auth_base_uri, sfmc_rest_base_uri, sfmc_suppression_list_mapping
8. Notifier#
| Operation | Read-only | Live Mode | Dry-Run |
|---|
notify_multipub_subscriber_followup_batch | Dry-run (logged, not sent) | LIVE — Angel + Yogesh To, Matt Cc (+ global Max + Vish) | Dry-run |
notify_tarun_undetermined_sender_review | Same | LIVE when undetermined-sender queue non-empty | Same |
notify_sai_action_items | Same | LIVE when manual-review bundle non-empty | Same |
notify_hodor_prospect_import | Same | LIVE when queued Hodor Prospect Import rows exist | Same |
notify_run_audit_for_ip4 | Same | LIVE every run — To: NOTIFICATION_EMAIL_SAI | Same |
notify_venu_cupola_audit_files | Same | LIVE every run — Venu (+ Sai Cc, Max for HR metadata) | Same |
notify_marketing_suppression | Same | LIVE when suppression rows exist — To: marketing team mailbox (NOTIFICATION_EMAIL_ERIN → marketing-team-cbi@columbiabooks.com) | Same |
notify_multipub_subscriber_followup_from_upload | Same | LIVE on Tarun upload Yes | Same |
_send_notification_email | Same | Global Max + Vish Cc on operational sends | Same |
- File:
src/auto_responder/services/notifications/notifier.py - Config:
Notifier(dry_run=True) in read-only and dry-run modes; recipient env vars include NOTIFICATION_EMAIL_VENU (N01 + N07), _ANGEL / _MATT / _YOGESH (Client Services Multipub routing), _VISH, _MAX, _SAI, _ERIN, _TARUN. See NOTIFICATIONS_REFERENCE for catalog numbering.
9. Action Engine#
| Handler | Read-only | Live Mode | Dry-Run | Notes |
|---|
_handle_inactive | LIVE logic; connector writes intercepted by wrapper | LIVE — CUPOLA flip subject to active-with-org gate + CUPOLA_AUTOMATIC_UPDATES; Hodor emits Prospect Import rows; Salesforce follows skip rules | Mocked connectors | |
_handle_active | LIVE logic; connector writes intercepted | LIVE — no auto-add, no auto-reactivate. No-CUPOLA rows route to IP4 handoff + Human Review. Inactive-CUPOLA rows route to Human Review as reactivation candidates. Hodor / non-Multipub Salesforce mirror reactivation still runs. | Mocked connectors | No-CUPOLA / reactivation rows queue for notify_sai_action_items (catalog N05/N06). |
_handle_title_update (EMAIL_UPDATE / TITLE_UPDATE) | LIVE logic; connector writes intercepted | LIVE — gated on active CUPOLA row. Inactive or missing CUPOLA blocks the update on every system and routes to Human Review. | Mocked connectors | |
_handle_replacement | LIVE logic; connector writes intercepted | LIVE — marks original contact inactive on CUPOLA (audited); replacement row written to output_document_alternate_contacts.csv. add_contact only fires when CUPOLA_AUTO_ADD_REPLACEMENTS=true. | Mocked connectors | |
_handle_out_of_office | LIVE logic | LIVE — no backend writes. Emits Human Review row with HUMAN_REVIEW_REASON_OUT_OF_OFFICE. | Mocked connectors | OOO is tracked as its own workflow; no CUPOLA / Hodor / Multipub / Salesforce calls. |
| Hodor updates from any handler | Intercepted via wrapper when WRITE_OPERATIONS_ENABLED=false | Routed to Prospect Import rows + notify_hodor_prospect_import (§8) | Mocked / graceful-unavailable | |
- File:
src/auto_responder/processors/action_engine.py - Config:
cupola_automatic_updates, cupola_auto_add_replacements in config.py; Recipient vars: notification_email_vish, _angel, _matt, _yogesh.
| Operation | Read-only | Live Mode | Dry-Run | Notes |
|---|
lookup_with_enrichment() / batch | LIVE | LIVE | Skipped — main.py builds a minimal Contact with empty records | Avoids meaningless mock data during dry-run |
- File:
src/auto_responder/processors/contact_lookup.py
Read-Only Wrapper Behavior#
When WRITE_OPERATIONS_ENABLED=false, ReadOnlyDatabaseWrapper (src/auto_responder/services/databases/readonly_wrapper.py) intercepts listed write operations for Cupola, Multipub, Salesforce, and Hodor (when the real connector is available).
| Intercepted method | Behavior |
|---|
update_contact_status() | Logged to action log, not executed |
update_contact_status_with_audit() | Logged to action log, returns (True, None) — previous_status cannot be observed in read-only mode |
execute_mark_person_inactive_sp() | Logged to action log, not executed |
execute_org_person_updates_sp() | Logged to action log, not executed |
add_contact() | Logged, returns mock ID, not executed |
update_contact_email() | Logged, not executed |
update_contact_title() | Logged, not executed |
update_lead_status() | Logged, not executed |
Reads pass through to the live database.
Note: Hodor update intents are emitted as Prospect Import rows at the application layer. WRITE_OPERATIONS_ENABLED=false still intercepts connector writes if any direct call path is introduced later. See connections/hodor.html.