The CRM as a Durable Work System

In trycompai/crm, the important architectural question is not where the model runs. It is who owns the decision when a CRM event needs interpretation. The design gives the API responsibility for recording what happened, gives the agent responsibility for deciding what it means, and gives the CRM database responsibility for preserving both the work and its result. That separation lets a reader trace one event from its origin to an evidence-backed record change without treating a background process as magic.

The API records an event, not its meaning

Consider a new contact. A conventional request handler might create the row, call an enrichment provider, wait for a response, and return a richer record. That shape looks direct, but it binds the lifetime of several different systems to one request. A slow provider makes the request slow. A temporary outage makes the write appear to fail. A process restart can leave the application unsure whether the outside call happened.

Comp AI CRM chooses a different boundary. The API owns HTTP, authentication, tRPC procedures, mailbox synchronization, and event reporting. It does not research, enrich, score, summarize, or match identities. When a CRM event requires one of those judgments, the API creates an AgentTask row. The row becomes the handoff between the transaction that knows an event occurred and the agent that can decide what to do about it.

This is more than a division of code into two directories. It is a division of authority. The API can say, “a contact was created,” or “a company changed,” because those are facts about its own transaction. It cannot safely say, “this is the person behind that email,” because that conclusion requires source interpretation. The agent reads the task, opens the relevant CRM record, gathers evidence, and invokes an authored tool to file the result.

The transaction boundary matters. The API can create a CRM change and its related agent task together when the event is emitted. If the transaction does not commit, the system does not leave a task describing a change that never became real. If the agent is down after the transaction commits, the row remains available. The CRM therefore preserves the question even when the component that answers it is temporarily absent.

The same pattern applies beyond contact identity. A new company can queue brand and profile work. A meeting can queue preparation. A workspace website change can queue a profile pass. The event tells the system which subject needs attention. It does not dictate the answer.

The task row is the system’s memory between processes

A durable task must carry more than a verb such as “research.” In the repository, AgentTask keeps the subject identifiers, task kind, reason, optional payload, priority, research budget, due time, lease, attempt count, session ID, start and finish timestamps, and outcome. Each field answers a failure question.

The subject identifier answers, “Which record is this about?” The kind distinguishes identity work from company work or a later recheck. The reason explains why the task exists. The payload carries structured event details when a simple kind is not enough. The budget limits how much metered vendor work one session can spend. The due time separates work that is ready now from work intentionally booked for later.

Consider what this changes after a contact is created. The API can commit the contact and the request to research it as one piece of durable work. If the agent is unavailable immediately afterward, the record does not lose the request. A later dispatcher can find the due row and see the same subject and reason. If a worker takes the row and stops, the lease eventually expires. The next attempt does not need to guess whether the first worker was still active; the lease and timestamps describe the claim. If the attempts end without a completion signal, the outcome says that the system retired the work. Each transition leaves a state that another process can interpret.

Those fields also separate intent from timing. A recheck booked for later is not an overdue failure. A task that is due but unleased is waiting. A task with an active lease is claimed. A finished task is no longer eligible for another claim. The database does not need one vague status label to cover all four conditions, because the columns describe the conditions that matter. That makes recovery a queryable property of the record rather than a private memory held by a worker.

The lease and attempt count answer a different class of question: “Who is working on this, and what happens if they stop?” The dispatcher claims due rows with a database update that sets a lease, records the start, increments attempts, and uses FOR UPDATE SKIP LOCKED while selecting the work. In plain terms, one worker locks the rows it is taking, while another worker skips those rows and looks for different work. Two dispatchers can therefore share a queue without handing the same live row to both.

A lease is not a promise that work succeeded. It is a temporary claim. When the lease expires, another attempt can take the row. When the task finishes, finishedAt and outcome make the result explicit. When the maximum number of attempts is exhausted without a completion signal, the task is retired with an explanation. The system does not convert “we tried several times and never heard back” into an eternal spinner.

The claim query also protects priority. Work that is due sooner or matters more can be selected first, while locked rows remain invisible to competing dispatchers. This matters when a queue contains several kinds of research. A system that processes rows in accidental arrival order can delay the work a representative needs most, even if every row eventually runs. Comp AI CRM makes urgency part of the persisted task instead of leaving it to the timing of whichever worker happens to wake up.

The reason field gives scheduled work a human meaning. A recheck is not merely “run again in fourteen days.” It can say that a job change would affect a live deal, or that a quiet record deserves a longer interval. That explanation becomes part of the work item a representative can understand. Scheduling is therefore a decision about future value, not only a timer setting.

The queue also avoids multiplying the same intention. When equivalent work is already open, scheduling moves the existing booking instead of creating a second row. The same principle applies when the API reacts to a repeated event: transactional idempotency prevents concurrent callers from quietly creating duplicate outstanding tasks. Deduplication does not decide whether a fact is true. It ensures that the evidence decision will not be obscured by several copies of the same request.

There is an important distinction between durable intent and durable execution. The row guarantees that the request to do work survives the original process. It does not guarantee that a vendor responds, that a model reaches a conclusion, or that every external side effect can be reversed. The repository uses leases, retries, idempotency, and outcomes to make those limits visible and recoverable. It does not pretend that persistence removes uncertainty.

The agent starts with CRM context

A task row tells the agent what needs attention, but it does not provide enough information to decide the matter. The research instructions require the agent to read the opened record before using paid or external sources. This ordering makes the CRM more than a destination for enrichment results. Existing interactions become the first research surface.

A contact read can include the contact’s name and address, the company and its identifier, related deals, recent email threads, message bodies or snippets, meeting attendance, reply statistics, upcoming meetings, and nearby colleagues. A company read can return its contacts with identifiers, deals, threads, meetings, and notes. A deal read can return its stage history and the people connected to it. Each read also gives the agent the IDs needed to continue into a related record.

That design solves a practical context problem. Suppose a company session reveals several people with similar names. The agent does not need to ask a representative to copy an internal identifier from another screen. The company read returns the contact IDs, and the agent can open the relevant history directly. If the contact has replied from the address under review, the CRM already holds evidence that a data vendor cannot recreate. If a signature block states a title, that internal observation can matter more than an external profile that is stale.

The links also prevent a common form of automation error: carrying the right conclusion to the wrong record. A company can have many contacts, a contact can appear on deals, and a deal can have several people around it. Returning neighboring IDs lets the agent move through those relationships explicitly. It can ask a question about the contact it actually opened, record a result against that contact, and preserve the company or deal as context rather than confusing context with identity.

The ordering also controls cost. Internal CRM reads are free in the agent’s research model. External vendor calls consume the session budget. Reading first lets the agent stop when the existing record settles the question, and it gives an external lookup the context it needs when the record is incomplete. A web search that begins with no internal context risks spending money to rediscover information the CRM already contains.

This is why the agent’s context is not just a prompt assembled from a task reason. It is a structured view of the CRM’s relationships. Companies, contacts, deals, threads, meetings, and colleagues form a connected record system. A research session that starts at one record can follow those links while preserving the identity of each record. The agent does not need to flatten the CRM into one undifferentiated text block before it can reason about it.

The result of that read is still not automatically a write. The agent must pass its observations to evidence-aware tools. The task supplies intent. The record supplies context. The evidence layer supplies the conditions under which a conclusion can alter the record.

The handoff in one sequence

The following sequence shows the architecture as a chain of ownership. The API owns the initial transaction. The database owns the durable handoff. The dispatcher owns the temporary claim. The agent owns interpretation. The CRM write path owns how the result is filed.

sequenceDiagram participant C as CRM change participant A as API participant Q as AgentTask table participant D as Dispatcher participant E as Agent session participant R as CRM history participant P as Rep C->>A: Create record or emit event A->>Q: Commit task with subject, reason, and budget A-->>D: Poke dispatch path D->>Q: Claim due row and set lease Q-->>D: Return task state D->>E: Start session with task context E->>R: Read record and related history first R-->>E: Threads, meetings, deals, and neighboring IDs E->>R: Submit an authored evidence-aware write R-->>E: Applied fact or proposal outcome E-->>P: Record result and unresolved context

The arrow from the API to the task table is the key design move. The API does not wait for the final answer before it acknowledges the original CRM change. The dispatcher can retry a task without recreating the business event. The agent can spend its budget against the same durable subject. The representative sees the outcome in the record system rather than in a transient request log.

Separation creates disciplined coupling

It would be a mistake to describe this architecture as fully independent services. The API and agent share the database, task vocabulary, event catalog, validation schemas, and record identifiers. That coupling is necessary. If one side called a contact person while the other expected contact, the queue could survive technically while losing its meaning.

Shared contracts also make changes more consequential. Adding a new task kind requires the agent to understand how to process it, the API to queue it correctly, and operational tools to report its state. Changing a payload shape affects every reader of that payload. The repository therefore keeps one owned vocabulary for tasks, events, records, and validation rather than letting each process invent its own meaning.

The benefit is a clear place to inspect a behavior. A reviewer can ask whether an API method only reports an event, whether a task has enough state to survive a retry, whether the agent reads CRM context before spending, and whether the write path records the evidence. Those questions are harder to answer when a request handler contains a little business judgment, a little vendor logic, and a little recovery code.

The cost is coordination. A durable agent does not remove the need for careful schema changes, compatible deployments, or operational monitoring. It changes where those concerns become visible. Instead of hiding a long chain behind one request, the system names the handoff and gives each process a part of the lifecycle.

That is the foundation for the next question: once the agent has the record and the task, what earns a conclusion the right to change the CRM? A durable queue can preserve work. It cannot decide whether the work discovered a fact, a useful clue, or nothing that should be written at all.

References