Aller au contenu

CRM Subsystem

Ce contenu n’est pas encore disponible dans votre langue.

AImetier’s CRM subsystem turns the platform into a control plane for outbound outreach in addition to coding work. Five channel adapters (Resend email, Cal.com, LinkedIn, WhatsApp via Twilio, Instagram Graph) share a single canonical OutboxDispatcher. The default posture is manual approval on every send. Auto-approve is opt-in per channel via graduated rules in crm_approval_rules.

Phases A–G shipped between 2026-05-23 and 2026-05-26 across 24 PRs (15 features + 7 audit fixes + 2 chore/bugfix). Every phase is independently revertable.

This page is the architecture index. Three deeper documents own different concerns:

Concern Doc
Tick-by-tick narrative, send-flow diagram, full table list, all audit fixes my_setup/docs/aimetier-explained/crm.html
API reference (route shapes, request/response payloads) docs/api/crm.md
Channel guardrails — allowlist, approval rules, atomic quota, brand-voice, content registry, outreach coverage, post-issue hooks my_setup/docs/aimetier-explained/guardrails.html#channel-guardrails
Cron tick walkthrough (CRM sequence runner) my_setup/docs/aimetier-explained/cron-jobs.html#crm-sequences
Scheduler architecture — per-tenant advisory lock, parallelism env var my_setup/docs/architecture-scheduler.md
Schema — six new tables + the promoted crm_activities.status column my_setup/docs/AIMETIER-PG.md
Channel credentials — secret://<id>:<v> convention my_setup/docs/SECRETS.md

Audit reports (committed alongside the phases)

Section titled “Audit reports (committed alongside the phases)”

These are point-in-time records of decisions and validation, kept for git-history continuity:

  • docs/crm-audit-2026-05-25.md — honest audit at end of Phase A. Every CRM page in HyperClip turned out to be already DB-backed; the real Phase A defect was a Drizzle Date-binding bug in connector-sync-worker.ts. Includes decision 0a (allowlist gates dryRun).
  • docs/crm-backfill-report-2026-05-25.md — Phase E backfill: 6,577 rows for the renovation niche, atomic-write run journal + lock per PR #22.
  • docs/crm-e2e-smoke-checklist-2026-05-25.md — Phase G manual UI smoke checklist (8 sections: Approvals, Outboxes, Approval rules preview + bulk-select, channel filters, Library, Forecasts, connector-sync-worker silent logs).
  • docs/crm-issue-compliance-rewrite-log.md — Phase F: 61 active outreach issues received ## CRM compliance section + 30 self-mention comments.
  • docs/crm-phase-g-summary.md — Phase G deliverables (3 e2e + 1 integration + manual checklist + --update-snapshots).
Phase PR What shipped
A #13 Connector-sync Date-binding fix + honest audit
B #14 5-channel outbound + dispatcher + 3 new tables + @aimetier/crm-channels package + manual-default approval engine + Approvals UI rebuild
C #16 Sequence cron with per-tenant advisory lock + 4 inbound webhook adapters + auto-pause-on-reply
D #17 crm_content_pieces + crm_brand_voice_cache + 4 routes + 5 MCP tools + 5 SKILL.md files
D-2 #19 Post-issue hook system: orchestrator + registry + 3 hooks + transition gates + admin-only bypass header
E #18 crm_activities.status first-class column + backfill scripts + HyperClip Library page
F #15 issue-compliance-update.mjs — added CRM compliance section to 61 issues, idempotent
G #20 8 e2e + 3 integration tests + manual smoke checklist

Seven hardening PRs landed alongside the feature work. The two CRITICAL fixes:

  • PR #22 — backfill journal atomic-write + lock. The Phase E backfill journal could be left half-written on mid-run crash, causing restart to re-apply already-processed rows. Fix: tmp + rename + per-script lock file.
  • PR #24dispatch.consumeOrBlock TOCTOU race. Quota used SELECT-then-UPDATE; two concurrent dispatches could both pass the SELECT then UPDATE-decrement past zero. Fix: single atomic UPDATE … SET sent_today = sent_today + 1 WHERE sent_today < daily_cap RETURNING * (returning zero rows = quota exhausted).

The remaining five hardening PRs (#21, #23, #25, #26, #27) cover orphan migration cleanup, content-piece size caps + UNIQUE constraint, hook AbortSignal cancellation + malformed-audit feedback, oracle.degraded propagation + cron tenant parallelism, and a bundled M+L audit fix (bulk-approve role check, activity+bucket atomicity, audit-log file lock, webhook PII redaction, timing-safe sig compare, sourcePath traversal validation, sha256 hex regex, tags cap, bypass-actor capture, YAML hot-reload admin endpoint, plus 6 LOW polish items).

Concern Path
Dispatcher server/src/services/crm/outbox/dispatch.ts
Channel adapters packages/crm-channels/src/{resend,cal-com,linkedin,whatsapp,instagram,dry-run}.ts
Sequence cron server/src/services/scheduler/cron-sequences.ts
Sequence runner / oracle / sink server/src/services/crm/{sequence-runner,oracle,sequence-action-sink}.ts
Approval engine + rules server/src/services/crm/{approval-engine,approval-rules}.ts
Inbound webhooks server/src/services/crm/inbox/{email-reply,linkedin,calendly,instagram}.ts
Post-issue hooks server/src/services/post-issue-hooks/{orchestrator,registry}.ts + hooks/
Hook YAML registry my_setup/post-issue-hooks.yaml
Backfill my_setup/scripts/crm-backfill/{index,state,util,sources}.mjs
Skills skills/{crm-content-audit,crm-content-register,crm-outreach-recorder,brand-voice-self-check,email-sequence-runner,social-poster}/SKILL.md
Var Default Purpose
AIMETIER_CRM_SEQ_TENANT_PARALLELISM 4 Max tenants processed concurrently per cron tick (PR #26)
AIMETIER_CRM_DRY_RUN false Force all sends through dry-run adapter (allowlist still applies)
AIMETIER_CRM_HOOK_TIMEOUT_MS 10000 Per-hook AbortSignal timeout in the orchestrator

The CRM subsystem is layered above the existing platform — it doesn’t change agent execution, the heartbeat scheduler, the storage 5-class model, or any of the ADRs (0001 platform-foundations, 0002 agent-workspace-contract, 0003 connector-and-ops). Channel adapters live in their own package (packages/crm-channels/) intentionally to avoid being confused with LLM adapters in packages/adapters/. Drafted outreach copy still flows through storage.putKnowledge (Knowledge class); crm_content_pieces rows reference those blobs without owning their lifecycle.

The post-issue hooks fire on the same PATCH /issues/:id and /issues/:id/request-review endpoints used by every other workflow. Bypass via X-AImetier-Bypass-Hooks: true is admin-only and audited.