In this piece
Odoo AI agent tests: validate saved activities
Suppose an agent says it scheduled the follow-up. A new activity exists, the right salesperson owns it, and the deadline looks correct. The activity is attached to a different opportunity. A test that checks only whether something was created would accept the wrong result.
Test an Odoo AI agent against a written business-state contract. Specify the activity it may create, the records it must leave unchanged and when it must ask for clarification. Then test the result checker against deliberately wrong observations before using it to judge controlled runs on the actual installation.
Research checked: 5 November 2026. Product facts use official Odoo 20 documentation and source. We executed thirteen constructed JSON observations through a Python checker. We did not run Odoo, an AI model or a mail server. The downloadable native acceptance pack contains proposed checks, all marked NOT_RUN.
What should an Odoo AI agent test prove?
An Odoo AI agent test should establish that the permitted business outcome occurred within the agreed observation scope. The requested write must be correct, existing protected records must remain unchanged, and prohibited effects must be absent from the evidence. A successful conversation or tool response alone cannot establish those conditions.
A result checker, sometimes called an oracle, is the rule that compares observed evidence with an agreed expected outcome. Keep that rule outside the agent’s own explanation of its work. If the agent chooses the target and also decides whether its choice was right, an initial misunderstanding can pass through both steps.
Odoo’s AI server-action documentation assigns business-rule enforcement to tool code, while the AI action selects the tool and arguments. The acceptance reviewer still needs to examine the resulting business state.
Separate this task-level decision from architecture. Our agent harness guide explains the surrounding execution controls. Here, the question is narrower: what evidence would let you accept one scheduled CRM follow-up?
How do you specify one CRM follow-up activity?
Specify a CRM activity with an unambiguous opportunity reference, an approved assignee and an absolute deadline. Then define which existing fields must stay unchanged. Our fictional contract permits one call activity for OPP-42 in company A, assigned to user 7, due on 12 November 2026; it prohibits external email.
Odoo’s activity scheduling documentation includes both an assignee and a due date. Those are separate acceptance conditions: the correct date does not compensate for assigning the wrong colleague.
| Condition | Expected result | Reason to reject |
|---|---|---|
| Target | One new call activity linked to OPP-42. | A similarly named opportunity receives it. |
| Company | The linked opportunity belongs to company A. | A reference in company B is used. |
| Assignee | Fixture user 7. | The caller or another salesperson is substituted. |
| Deadline | 2026-11-12. | The observed date is 2026-11-13. |
| Protected state | The selected opportunity fields and existing ACT-OLD activity remain unchanged. | The opportunity stage or an existing deadline changes. |
| External effect | No outbound email event in the declared observation scope. | The task produces an unapproved external message. |
| Ambiguous request | Ask which opportunity to use and make no business write. | Guess an opportunity and create an activity. |
The fixture also contains OPP-43 in company A and OPP-99 in company B. The pre-existing activity ACT-OLD belongs to OPP-43, is assigned to user 8 and is due on 10 November. These nearby records make a wrong match and an unrelated update visible.
The download uses normalized keys such as record_ref and assigned_user. They are not an Odoo API payload. In the inspected Odoo 20 activity source, document linkage uses model/reference fields; assignment and deadline use user_id and date_deadline. A native observer must map the installed schema into the agreed projection.
In this example, company belongs to the linked opportunity. The checker follows that relationship; it does not invent an activity-level company field. The projection also excludes technical bookkeeping such as chat records. Decide explicitly which additional business fields and notification effects your installation needs to observe.
Why must the result checker be tested first?
The result checker needs negative examples because a weak rule can make an incorrect workflow look reliable. Agree the expected classification before running the checker, and include outcomes where the requested activity is correct but another protected record changes. This tests the acceptance rule’s meaning, rather than its ability to recognize a happy example.
Our fixture labels are written separately from the checking function. The function reads the contract and before/after projections, then returns PASS, FAIL or UNKNOWN with reasons. It does not use the supplied assistant text or reported tool status as proof that a write occurred.
For example, a count-only checker would accept two activities if it merely required “at least one.” A field checker that searches only for the requested new row could miss an altered existing activity. The acceptance rule therefore compares the protected baseline as well as the new row.
That independence has a practical limit. Someone still has to approve the contract and validate the observer that builds the projection. A checker cannot detect a field its input omits, or prove that a file labelled “complete” contains every relevant side effect. Its scope must be visible in the release evidence.
What did the thirteen fixture checks show?
All thirteen constructed observations received their expected classification in our local checker run: two PASS, ten FAIL and one UNKNOWN. This establishes the behavior of the supplied Python demonstration on those inputs. It does not measure how often an Odoo agent succeeds, because no agent generated the observations.
| Case | Constructed final observation | Expected / observed |
|---|---|---|
| S01 | The correct new activity; protected state unchanged. | PASS / PASS |
| S02 | A completion message, but no new activity. | FAIL / FAIL |
| S03 | The activity points to OPP-43 in the same company. | FAIL / FAIL |
| S04 | The activity points to OPP-99 in company B. | FAIL / FAIL |
| S05 | The new activity is assigned to user 8. | FAIL / FAIL |
| S06 | The deadline is 13 November instead of 12 November. | FAIL / FAIL |
| S07 | Two new activities are created. | FAIL / FAIL |
| S08 | The correct activity exists, but OPP-42's stage becomes won. | FAIL / FAIL |
| S09 | The correct activity exists alongside a synthetic outbound email event. | FAIL / FAIL |
| S10 | An ambiguous request leads to a guessed target and a write. | FAIL / FAIL |
| S11 | The ambiguous request gets the required clarification; no business state changes. | PASS / PASS |
| S12 | The final observation is unavailable. | UNKNOWN / UNKNOWN |
| S13 | The correct new activity exists, but ACT-OLD’s deadline changes. | FAIL / FAIL |
Download the checker and case file into one directory. Python’s standard library is sufficient:
python3 state-oracle.pyCompare the output with the recorded results. The process exits successfully when all expected classifications match, including the deliberate FAIL cases. A successful script exit therefore means the checker recognized the supplied examples; it does not mean thirteen business tasks passed.
The clarification branch uses an illustrative reviewed_disposition label. In a real test, a reviewer or separately validated adapter must establish that the agent asked for the missing opportunity reference. The script does not understand the quality of a natural-language question, and this label is not a documented Odoo response field.
When is UNKNOWN the correct result?
UNKNOWN is correct when the observer cannot establish the required final state. An inaccessible record, incomplete side-effect evidence or an unresolved observation window leaves the acceptance question open. Preserve what is known and investigate the observation failure before deciding whether the business action needs recovery; a missing read is not proof that no write happened.
| Observation | What it can establish | What remains open |
|---|---|---|
| Complete agreed business projection | Whether the included records satisfy the contract. | Excluded fields and external effects. |
| Tool response or chat text | What the system reported. | Whether the expected result persisted. |
| Controlled mail-sink and transport evidence | Observed message attempts within the tested route. | Any unobserved connector or alternate route. |
| Failed or incomplete final read | The evidence is insufficient. | The business outcome, which stays UNKNOWN. |
The synthetic outbound_events list is only a stand-in for external-effect evidence. An empty list cannot enforce a mail restriction. In a native rehearsal, route messages to an approved test destination and inspect the relevant transport evidence. If activity-assignment notifications conflict with the no-email requirement, resolve that behavior before accepting the workflow.
Choose an observation window that covers the task’s actual completion boundary. A queue still working at the cutoff is unresolved, not automatically successful or permanently failed. Record the query scope and cutoff with the result so the reviewer can distinguish late work from missing work.
Do not repeat an uncertain write to obtain a friendlier response. The Odoo write-recovery example explains the duplicate risk and recovery decision separately.
Which tests still need the actual Odoo installation?
Native acceptance must exercise the configured agent and its available tools on the actual Odoo build under review. Use approved test identities and verify the observation adapter against known records. Then compare the resulting evidence with the contract, keeping the fixture checker’s success separate from the agent’s ability to perform the task.
The native acceptance pack starts with sixteen proposed cases. Execution status is NOT_RUN, acceptance is PENDING, and observed values are empty. Adapt the cases to your installed modules and task before recording a result.
| Test area | Evidence to capture | Decision it supports |
|---|---|---|
| Observation adapter | Known native records compared with the normalized projection. | The checker reads the intended fields and relationships. |
| Ordinary task | Initial snapshot, exact request, final activity and protected records. | The configured agent can complete the permitted task. |
| Identity and company | Agent user, company context, allowed target and refused alternative. | The run uses the intended authority. |
| Ambiguity | A request matching two opportunities, the clarification and unchanged business state. | The workflow waits for missing information. |
| External effects | Controlled delivery route and observed message attempts. | The actual notification behavior meets the contract. |
| Interrupted observation | Known run identity, incomplete evidence and the recovery decision. | Unknown outcomes do not become blind retries. |
| Changed configuration | Build/tool/prompt identity and the repeated accepted cases. | A previous result still applies to the revised workflow. |
Use the launch QA checklist for the wider release decision. This task pack addresses the activity workflow and its observation evidence.
The observer needs authorized access to the test population. Its broader view, if required for reconciliation, must not be confused with the agent’s operating permissions. The separate Odoo 20 company-access tests own that native authorization matrix.
Record model or provider metadata when the tested route exposes it; mark unavailable details as unavailable. Preserve the tool configuration and prompt revision even when the model identity is opaque. A test record should describe the observed configuration and mark any unverified service details.
How should you review repeated agent runs?
Review repeated agent runs individually before summarizing them. Each record needs the starting fixture, exact request and observed outcome, with a reason for any FAIL or UNKNOWN. Agree the repetitions and acceptable variation for the task’s consequences before execution; the thirteen checker examples provide no statistically justified production threshold.
Start repeated trials from an equivalent baseline unless the case explicitly tests existing work. Otherwise, the second run may encounter yesterday’s activity and test a different question. If repeated delivery is part of the workflow, define whether it should reuse an existing activity, ask for confirmation or create another one.
Track business-state correctness separately from communication quality. An agent can leave the correct record while giving the user an unhelpful explanation. Conversely, a polished explanation cannot rescue a wrong assignee. Both matter, but combining them into one impression makes defects harder to diagnose.
Keep a forbidden external write visible as its own failed condition. It should not disappear inside an average of easy tasks. For contractual measures and response ownership, use the agent SLA template; this article supplies one bounded task’s acceptance evidence.
What would we approve for release?
We would approve only the bounded activity task whose expected outcome, observation scope and native evidence have been accepted by the responsible owner. The fixture checker is useful preparation because it exposes weak acceptance rules cheaply. It becomes release evidence only alongside successful observation of the actual configured workflow and its required failure handling.
Begin with the ordinary activity and the wrong-target counterexample. Add the case where the new row is correct but an existing activity changes. That combination forces the team to state both what the agent may do and what must remain protected.
If you need an independent release decision, Wavect’s software quality assurance service can help define and review that evidence. Bring the task contract, your installed configuration and the cases that remain unresolved. A green demonstration is most useful when the reviewer can explain exactly what it proves.
