Skip to content

Repository files navigation

Compliance Studio

A local-first accounting and compliance cockpit for a UK owner-managed limited company — where a language model reviews the work but is never allowed to do it.

Compliance Studio pulls a company's complete accounting history from FreeAgent, cross-checks it against the public Companies House register, runs every statutory calculation deterministically, and then hands the whole picture to a locally hosted language model for review and explanation.

The model can analyse, explain, draft and suggest. It cannot file a return, move money, change FreeAgent, approve a dividend, or mark a compliance obligation complete.

Dashboard


Why this repository exists

Most "AI for X" projects put the model in the critical path and hope the prompt holds. In a regulated domain that is the wrong shape. Corporation tax is not a generation problem — it is arithmetic with a legal definition, and a plausible-sounding wrong answer is worse than no answer.

So the interesting engineering here is not the model call. It is the boundary:

Concern Owner Why
Corporation tax, NI, Self Assessment, s455, marginal relief Deterministic Python + versioned rule pack Legally defined; must be reproducible and auditable
Deadline tracking, evidence retention, audit history SQLite, append-only Must survive restarts and be provable after the fact
Transaction categorisation Deterministic engine, ranked by historical evidence Every suggestion carries its confidence and its basis
Explanation, review, anomaly spotting, drafting Local LLM Genuinely open-ended; a wrong answer is cheap because it cannot act
Deciding, approving, filing, paying The human director Statutory responsibility is not delegable

Everything below follows from that table.


Architecture

flowchart TB
    subgraph external["External systems"]
        FA["FreeAgent API<br/>OAuth 2.0"]
        CH["Companies House API<br/>read-only"]
    end

    subgraph local["Local machine — no data leaves it"]
        SYNC["Sync layer<br/>full → incremental → deep resync"]
        DB[("SQLite<br/>lifetime archive,<br/>evidence, audit log")]
        CALC["Deterministic engine<br/>CT · NI · SA · s455 · dividends"]
        RULES["Versioned tax-rule pack<br/>verified / expires dates"]
        CAT["Categorisation engine<br/>rules → history → similarity"]
        QUEUE["Action Queue<br/>priority · confidence · evidence"]
        LLM["Local Ollama model<br/>review · explain · suggest"]
        UI["PySide6 desktop UI<br/>24 workflow tabs"]
    end

    HUMAN(["Director<br/>approves, files, pays"])

    FA -->|"pull"| SYNC
    CH -->|"verify"| SYNC
    SYNC --> DB
    DB --> CALC
    RULES --> CALC
    DB --> CAT
    CALC --> QUEUE
    CAT --> QUEUE
    QUEUE --> UI
    DB -.->|"full context,<br/>untruncated"| LLM
    CALC -.-> LLM
    LLM -->|"advice only"| UI
    UI --> HUMAN
    HUMAN -->|"approves"| DB

    classDef ext fill:#e8eef7,stroke:#5b7ba8,color:#1a2b42
    classDef ai fill:#f5eee6,stroke:#b08a5b,color:#402c14
    classDef human fill:#e6f2ea,stroke:#5b9e78,color:#123322
    class FA,CH ext
    class LLM ai
    class HUMAN human
Loading

The dotted lines are the only paths the model touches, and they all point inward. There is no edge from the model to any external system, to the database, or to a filing.


The integration decisions worth reading

1. The model is given everything, and trusted with nothing

The local adviser receives the complete current data pull with no application-level truncation, plus the lifetime history summary, the deterministic calculations, the evidence index and the Companies House cross-check findings. Context size is computed from the payload rather than capped at a convenient number.

That is a deliberate inversion of the usual trade-off. Because the model has no ability to act, there is no reason to be defensive about what it sees — and giving it partial data is how you get confidently wrong answers about a set of accounts.

Local adviser

2. Local inference only — there is no cloud path to remove

The only supported endpoint is a local Ollama instance. There are no cloud providers, no external model API keys, and no remote fallback anywhere in the codebase. This is enforced by the test suite, not just by convention:

pass cloud LLM settings removed
pass local Ollama client only
pass context explicitly untruncated

For accounting data this is the difference between a tool a director can actually use and one their insurer would object to.

3. Categorisation is evidence-ranked, not model-guessed

For each unexplained bank transaction the deterministic engine checks, in order: user-approved rules → exact prior payee matches → description and counterparty similarity → debit/credit direction → historical amount consistency → frequency and category consistency.

Every suggestion surfaces its confidence and the historical basis behind it. The model is asked to review the ranked shortlist; it never invents the category. Nothing is written back to FreeAgent automatically — the user promotes a suggestion to a deterministic rule, or does not.

Transaction review

4. Statutory rules are a versioned artefact with an expiry date

Tax rates are not constants in the source. They live in a signed-off rule pack carrying a version, a verified-on date, an expiry date and source citations. The UI shows the pack's freshness state, and calculations built on an expired pack say so.

This is the compliance-domain answer to the grounding problem: the authority for a number is a dated, checkable artefact rather than whatever was in the training data.

5. Reconciled, not merely estimated

The app distinguishes a figure it has reconciled from one it has estimated, and refuses to present the second as decision-ready. Statutory calculations use the legally relevant period with explicit carry-forwards; lifetime averages are used for forecasting and anomaly detection, and are never mixed into the tax arithmetic.

CT reconciliation

Reconciliation is only meaningful if you know what is missing, so every source stage reports its own completeness, record count and provenance:

Data completeness


Integrations

FreeAgent — OAuth 2.0 with a local loopback callback. Full lifetime history sync (bank transactions, invoices, bills, expenses, credit notes, estimates, journals, contacts, projects, capital assets, recurring invoices, payroll years, annual trial balances), then incremental pulls with a periodic deep resynchronisation to catch retrospective corrections.

A notable piece of real-world API defensiveness: FreeAgent's Company endpoint advertises annual_accounting_periods that the Trial Balance endpoint will then refuse with HTTP 400 — No accounting period includes the specified dates. Compliance Studio probes candidate dates inside each declared period, indexes only what the API actually accepts, records the provenance, caches unsupported periods for 180 days, and reports the gap as non-blocking rather than failing the pull.

Lifetime history

Companies House — read-only public register verification: company profile, accounts and confirmation-statement due dates, filing history, officers, PSCs, charges and insolvency data. Cross-checked against local settings, with material differences and overdue public filings raised into the Action Queue. Never used as a bookkeeping source or as an input to tax arithmetic.

Both integrations, the deterministic checks and the categorisation engine converge on one queue, where every row carries its priority, its due channel, its confidence and the evidence it needs:

Action queue


Security and data handling

  • Accounting data never leaves the machine. Inference is local.
  • On Windows, the FreeAgent OAuth secret and tokens and the Companies House API key are protected with DPAPI; other platforms use the OS keyring.
  • Ordinary preferences live in ~/.compliance_studio_settings.json; records, snapshots, evidence and audit history live in ~/.compliance_studio.db. Neither is in this repository.
  • Every source pull is snapshotted immutably with a content hash, and decisions (such as a dividend) are frozen against the exact snapshot and inputs that justified them.
  • The audit log is append-only.

Verification

python compliance_studio.py --selftest
65 v4 checks, 0 failure(s)
python compliance_studio.py --guitest
24 GUI checks, 0 failure(s)

The headless suite covers history ingestion and old-date coverage, FreeAgent metadata/report mismatches, non-blocking trial-balance source gaps, deterministic categorisation, full raw-history context assembly, Companies House cross-checking, current statutory values, deadline persistence, CT bands and marginal relief, Self Assessment reconciliation, dividend snapshots, local-model context construction and SQLite persistence. The GUI suite builds all 24 tabs offscreen and asserts they render against the demo dataset.


Running it

Requires Python 3.10+.

python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt

Explore the whole application on canned data, with no FreeAgent account and no API keys:

python compliance_studio.py --mock
Command Effect
python compliance_studio.py GUI against live FreeAgent data
python compliance_studio.py --mock GUI on the demo dataset
python compliance_studio.py --pull Headless live pull and text report
python compliance_studio.py --ask "question" One local-model answer, headless
python compliance_studio.py --selftest Regression suite
python compliance_studio.py --guitest Offscreen GUI smoke test

For the local adviser, install Ollama and pull a model with a large context window:

ollama pull qwen3:30b

A full-history review can require a very large context and substantial RAM/VRAM. Ordinary questions use the current pull plus a lifetime summary and task-relevant history.

Connecting live data requires a FreeAgent developer application with the callback http://localhost:8582/callback, and optionally a Companies House Developer Hub API key. Both are entered in Settings and stored through the OS credential backend.


Demo data

Everything in --mock mode is a fictional company (Example Consulting Limited, registration 99999999) with invented figures, contacts and transactions. No real company, client, bank or financial information appears anywhere in this repository.


Scope and disclaimer

This application does not submit filings, make payments, or alter FreeAgent. It is decision support for a director who remains responsible for approving actions and retaining evidence.

Treat a result as decision-ready only when its source stages are complete, the tax rules are verified and unexpired, the required inputs and evidence are present, the figure is reconciled rather than estimated, and the director has reviewed it.

This is not tax advice and is not a substitute for a qualified accountant. For unusual transactions, complex groups, overseas matters, share reorganisations, insolvency risk, large capital gains, R&D claims or disputed tax positions, obtain specialist advice.


Licence

MIT.

About

Local-first UK limited-company accounting and compliance cockpit. A deterministic engine owns every statutory calculation; a locally hosted LLM reviews the work but is never allowed to do it.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages