Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.ntropii.com/llms.txt

Use this file to discover all available pages before exploring further.

Quality checks let a runbook ask a separate model to evaluate whatever the previous step produced — extracted invoice fields, a proposed journal, a calculated NAV — before a human ever sees it. The check result feeds the Tenant UI’s review screen alongside the underlying value, so the accountant reviewing the output sees the AI’s own assessment of “I’m pretty sure I got this right” or “this looks off, please double-check the GL mapping”.

Install

pip install 'ntro[workflow]'
The capability is bundled with the workflow extra.

The API

from ntro.capabilities.checks import run_quality_check

check_result = await run_quality_check(
    payload=...,                     # Whatever you're checking
    check_slug="journal-balance-v1", # Routes to a check definition
    context={...},                   # Optional grounding info
)
Returns a structured CheckResult with:
FieldTypeWhat’s in it
result.passedboolThe headline answer
result.severity"info" | "warn" | "error"How urgent the finding is
result.summarystrOne-line human summary for the UI
result.findingslist[Finding]Itemised observations, each with field path + explanation
result.suggested_correctionslist[Correction]Optional suggested edits the human can accept with one click

Wiring into a runbook

Quality checks are typically run as a Temporal activity right after the thing they’re checking. Lifted from nav-monthly-journals:
from temporalio import activity

from ntro.capabilities.checks import run_quality_check
from ntro.workflow.history import (
    find_committed_document_activity,
    load_previous_task_activity,
)

# Re-export the SDK's check + history activities so the runbook bundle's
# activities discovery picks them up. Without these re-exports, the worker
# only registers locally defined activities and check calls fail with
# "activity not registered".
__all__ = [
    "parse_starting_tb",
    "propose_journal",
    "commit_journal_proposal",
    "run_quality_check",
    "load_previous_task_activity",
    "find_committed_document_activity",
]
The re-export pattern matters. run_quality_check is implemented in the SDK, but the worker only registers activities it can find via the runbook’s activities.py. Re-exporting run_quality_check (and the other shared activities you need) in __all__ is what wires it into your bundle.
The actual call site is just await run_quality_check(...) from inside the workflow:
@workflow.defn
class NavMonthlyJournalsWorkflow(NtroWorkflow):

    @ui_step(name="check_proposal", title="Quality check", icon="ShieldCheck")
    async def _step_check(self, proposal: JournalProposal) -> CheckResult:
        return await workflow.execute_activity(
            run_quality_check,
            args=[
                {"payload": proposal, "check_slug": "journal-balance-v1"},
            ],
            start_to_close_timeout=timedelta(minutes=5),
        )
The Tenant UI renders the CheckResult next to the underlying JournalProposal — the accountant sees both the proposed journal and the model’s “I checked the balance, debits = credits, looks balanced” alongside it.

Designing a good check

Quality checks work best when they’re focused. A check that’s “review this entire NAV” is hard to interpret. A check that’s “verify the journal balances and that all GL codes exist in the COA” is actionable. Common shapes:
Check typeExample slugWhat it does
Constraint checkjournal-balance-v1Asserts a structural property (debits = credits, all dates in period, etc.)
Cross-reference checkgl-map-coverage-v1Confirms every account in the proposal exists in the COA
Sanity checknav-plausibility-v1Compares the result to historical norms, flags outliers
Tail checkaudit-anomaly-v1Looks for the unusual — duplicate references, suspicious round numbers, weekend-dated entries
Each slug routes to a check definition in your provider configuration. The same model and prompt setup used by ai.extract backs run_quality_check.

Severity drives routing

The severity field affects how the Tenant UI surfaces the result:
  • info — green check, accountant sees “all clear” and approves quickly
  • warn — yellow flag, accountant sees the finding inline with the value
  • error — red block, the workflow can wait_for_action with must_resolve=True to force a correction before approval
Pair severity == "error" with wait_for_action(must_resolve=True) to guarantee the human can’t approve over the top of a critical issue.

Workflow capability

Where you wire the check into the run via @ui_step.

Private AI

The same provider plumbing backs both extraction and checks.