Skip to content

How it works

Seven stages, none of them opaque

A file goes in and a set of evidenced findings comes out. Each stage is separate, inspectable, and free of anything that could vary between two runs of the same input.

The pipeline

  1. 01

    Import

    A format adapter reads the file into raw sheets: headers, rows, column labels and the original row numbers. Adapters never interpret a cell. A value beginning with = is data, not a formula.

  2. 02

    Normalise

    The column mapping turns raw cells into the internal transaction model, parsing dates, amounts and rates. Anything unreadable becomes a reported issue rather than a silent default.

  3. 03

    Fingerprint

    A SHA-256 digest is taken over a canonical serialisation of the normalised records, identifying the dataset independently of the file's name or where it came from.

  4. 04

    Validate

    Every enabled rule is evaluated in order against the records. Rules are pure functions: no clock, no network, no shared state.

  5. 05

    Reconcile

    A second record of the same events. A bank statement, or the detail behind a control account. Is matched against the ledger in passes over an amount index. Two amounts are compared only when their currency is known and either identical or reconciled through a rate the data carries.

  6. 06

    Evidence

    Each finding is stored with the attributes compared, the chain of events, the rule version that produced it and the source cell behind every value.

  7. 07

    Carry forward

    The next period inherits configuration and written explanations, and inherits no transaction, finding or run. What carries and what does not is shown in full before anything is created.

Determinism

Given the same dataset, the same rule versions and the same configuration, VLDTION produces the same findings, including the same identifiers. A finding's id is derived from the rule, its version and the records involved, so it can be referenced across runs, carried forward through review, and cited in an export.

  • Rules never read the current time; they are given the dataset's import timestamp.
  • Rules never perform I/O, so a run cannot depend on anything outside the data.
  • Iteration order is fixed, and findings are sorted by severity, then date, then id.

The rule contract

Every rule implements one interface, and the conditions shown in the product are the ones the engine ran:

interface RuleDefinition {
  id: string
  name: string
  category: RuleCategory
  severity: Severity
  version: string
  conditions: readonly string[]
  enabled: boolean

  evaluate(context: RuleContext): readonly FindingDraft[]
}

A rule set is itself versioned: the identifier stamped on a run is derived from every enabled rule's id and version, so changing any rule changes the rule-set version and the change is visible in the run history.

Architecture

UI               screens, components
Application      import, runs, evidence, configuration
Domain           rules, findings, evidence, models
Infrastructure   file adapters, SQLite, desktop shell

The domain has no knowledge of storage and the interface has no knowledge of SQL. Storage sits behind one repository interface, which is why the same product runs against a SQLite file on the desktop and against SQLite compiled to WebAssembly in a browser.