High-level flow
Run responsibilities
Current execution paths
A run starts from one of two entry points. After the trigger, both execute the same stages and the same verification passes.
The verification decisions live in one place,
darpan.facade.reconciliation.RunVerificationSupport, so a scheduled run and an interactive run apply the same rechecks. On the automation path, one runtime switch can turn all verification off for an instance.
Separately, a run’s compare definition takes one of two shapes:
Darpan supports both. New run contracts center on
RuleSetCompareScope; existing services and release history may still reference mappings.
Scope modes, flowcharts and lifecycles
A compare scope has ascopeMode. Null or COMPARE is the two-sided compare above. EVALUATE is single-sided: evaluate#RuleSetCompareScope reads one source and its rows are the findings, in the missing-diff row shape. The interactive path forks once, early, and EXTRACT_FILE2 is never opened. An unknown mode, or a FILE_2 source on an EVALUATE scope, is refused.
A run flowchart is a Reconciliation whose questions form a parent-first tree. Each question is one RuleSet run; the questions under it run on its yes or no branch, and a parent’s keys narrow the child’s FILE_1 at prepare, so each question answers only for the records that reached it. run#Reconciliation and an automation whose savedRunType is reconciliation walk the tree per window (darpan.reconciliation.flowchart.RunFlowchartWalker). A question under one that failed or was switched off ends AUT_STAT_NOT_RUN. import#NetSuiteChecksAsRuns turns a tenant’s NetSuite checks into flowcharts: one START per population, each check a yes question under it. It is safe to repeat, and says why any check was skipped.
A lifecycle is a flowchart compiled by save#Lifecycle from a NetSuite account, a start record type, and its stages in order. Every record lands in exactly one named place. Leave-out rules, stage exceptions (waiveRules), written findings (matchRules) and places marked as not a finding are stored on the lifecycle and recompiled into the tree. A lifecycle walk reads its window once (darpan.reconciliation.core.RunSnapshotScope); switch that off with -Ddarpan.reconciliation.runSnapshot=off, and every question reads live.
Conclusions
When a scope has conclusion rules, theCONCLUDE stage names every verified finding from a per-run Drools tree (darpan.reconciliation.conclusion.ConclusionTreeEngine) and stores the evidence the deciding rule tested. A finding no rule explains is UNEXPLAINED. Conclusion catalogues and rules are tenant configuration, loaded per tenant, not seed data.
Run stages and observability
A run’s progress is recorded so an operator can see where it is, and where it stopped.darpan.facade.reconciliation.RunObservability is the single instrumentation point for both entry points.
Each run has one ReconciliationRunResult row. Its statusEnumId moves from AUT_STAT_PENDING or AUT_STAT_RUNNING to one terminal status: AUT_STAT_SUCCESS, AUT_STAT_FAILED, AUT_STAT_NO_DATA, AUT_STAT_SKIP_DUP, AUT_STAT_CANCELLED, or AUT_STAT_NOT_RUN (a flowchart question under one that failed or was switched off). The row also carries currentStage, a heartbeat date, and windowTimeZone, the zone the run window was anchored in.
Each stage writes one ReconciliationRunStep row with its own status, timing, and metrics. Stages run in this order (RunObservability.STAGE_SEQUENCE):
Verification runs before
WRITE_OUTPUT, so the file a user downloads is the corrected one. Each pass has its own stage code and records the systems it rechecked in its own step metrics. A pass that declines to run records why.
Rows written before 2.0.0 carry a single VERIFY code. It keeps sequence 5, so old runs still sort into their original slot; their WRITE_OUTPUT and NOTIFY rows keep sequences 6 and 7. New runs never enter VERIFY. A consumer that reads stageCode or stageSequence must accept both sets.
Stuck-run recovery
If a JVM crash or container restart kills a run mid-flight, its run-result row can be left in a running state forever, which blocks re-triggering. An internal scheduled reaper handles this:ReconciliationRunResult and ReconciliationAutomationExecution rows still in running or pending state, whose last update is older than a threshold (staleMinutes, default 120), to failed with an explanatory status message. The service is internal only (allow-remote="false") and is not part of the JSON-RPC surface.
RuleSet compare flow
The RuleSet path is built from these records:RuleSetRuleSetCompareScopeRuleSetCompareSourceRuleReconciliationRun- Generated output descriptor records
- The facade service creates or loads a saved run and its compare scope.
- Generic reconciliation saves uploaded or text payloads in a tenant-scoped temp location.
prepare#RuleSetCompareScoperesolves compare sources and builds normalized Spark datasets.reconcile#RuleSetCompareScopeBaseDifffinds base missing-object differences and matched pairs.reconcile#RuleSetCompareScopepreserves base diffs and appends rule-generated diffs from matched pairs.- The backend writes result data and summary metadata as a generated output.
File and source handling
The backend can compare uploaded files, text payloads, and configured sources. CSV and JSON inputs are normalized before comparison. The comparison layer receives structured data and saved run context, not UI-specific state. Generic file reconciliation is routed through:Generated outputs
Generated outputs are not only downloaded files. The backend stores descriptor metadata so the UI can list, open, and delete outputs consistently. Generated-output metadata can include:- Saved run ID and display name
- RuleSet and compare-scope IDs
- Source labels
- Output location
- Diff counts and summary fields
- Created user and tenant scope
Run-completion notifications
Runs that reach a terminal state alert the owning tenant so sync issues surface without anyone watching the run history page. Destinations are resolved per run rather than per tenant, which is what lets one person follow a single run without changing shared configuration.- A run reaches a terminal state through one of three paths: a manual saved-run diff (
facade.ReconciliationFacadeServices.run#SavedRunDiff), a scheduled automation execution, or SFTP polling. Terminal states areAUT_STAT_SUCCESS,AUT_STAT_FAILED,AUT_STAT_NO_DATA,AUT_STAT_SKIP_DUP, andAUT_STAT_CANCELLED. - The completion path calls
darpan.reconciliation.notification.TenantNotificationSupport, which resolves destinations throughresolveDestinationChatSpaces: the automation’s linkedchatSpaceId, plus the snapshotted space on everydarpan.reconciliation.ReconciliationRunNotifySubscriptionrow for the run. - That set is deduplicated and filtered to
darpan.reconciliation.TenantChatSpacerows that are active, can deliver, and belong to the run’s own tenant. A dropped automation-linked space is logged as a warning; a dropped subscriber space is expected churn and stays silent. - The helper builds the message through
reconciliation.ReconciliationNotificationServices.build#RunCompletedPayloadand posts it to each surviving destination.
DARPAN_APP_BASE_URL environment variable or the darpan.app.baseUrl property, falling back to the first allowed origin.
Automation executions have one more end state. An execution that fails with a transient error is retried; one that exhausts its retries ends in AUT_STAT_DEAD_LETTER and sends a give-up alert to the same destinations. facade.ReconciliationFacadeServices.reprocess#AutomationExecution resets a failed or dead-lettered execution to pending with a fresh retry budget.
Chat providers
A chat space posts to one provider, recorded inchatProviderEnumId:
The webhook URL is validated on save:
- Google Chat:
httpsonchat.googleapis.com, a/v1/spaces/…/messagespath, andkeyandtokenquery parameters. - Slack:
httpsonhooks.slack.com, a complete/services/…path.
TenantChatSpace.webhookUrl, and list#TenantChatSpaces returns them to members of the tenant. Treat read access to a tenant’s settings as access to its webhook URLs. Slack bot tokens belong to the workspace install and are not returned.
The registry, the per-user default, and Slack setup are configured through:
save#TenantChatSpace requires tenant write access; save#UserNotificationDefault and the subscribe pair are personal preferences and do not. delete#TenantChatSpace hard-deletes only unreferenced spaces — a space an automation or subscription points at must be deactivated instead. A Slack space can only name a workspace install that belongs to the same tenant.
The single-webhook darpan.reconciliation.TenantNotificationSetting entity this replaced is retired. reconciliation.ReconciliationNotificationServices.migrate#TenantNotificationSettings is the one-time upgrade that copies each tenant’s old webhook into a space named Default space and links that tenant’s space-less automations to it. It is idempotent, internal only (allow-remote="false"), and registered as a data migration. The product-side setup flow is described in Manage tenant settings.