TotalApp Docs

Compliance Engine

A Strategy Pattern engine that resolves a jobsite's country into the right permit/zoning paperwork — then hands the resulting form back to the frontend using the exact same server-driven-UI schema shape the UI Rendering Engine already produces, so no new rendering code was needed to display it.

What Is the Compliance Engine?

Construction paperwork is not the same everywhere. A Turkish jobsite files Ada, Parsel and Pafta cadastral references, a "Müteahhit SGK Sicil No" contractor registration number, a site manager's certificate number, and TAKS/KAKS floor-area ratios. A US jobsite instead files a Parcel ID, a Contractor License Number, an OSHA Certification Number, and a Zoning Classification. Hardcoding one form per country inside every screen that needs permit paperwork would mean re-deriving the same jurisdiction logic in three different places — and getting it wrong in at least one of them.

The Compliance Engine centralizes that logic behind a single Strategy Pattern: a PermitStrategy interface with one method, getPermitFormSchema(permitType), and one concrete class per jurisdiction — TRPermitStrategy, USPermitStrategy, and a DefaultPermitStrategy fallback that never throws for a country nobody has written a strategy for yet. resolveComplianceStrategy(countryCode) upper-cases the incoming code and switches between them. Adding a new jurisdiction is writing one new strategy class, not touching any of the screens that call this engine.

The engine's output is not raw HTML or a hardcoded React form — it's a UiComponent tree, the same server-driven-UI shape the UI Rendering Engine defines and other engines already produce. Compliance Engine directly reuses UiComponent/UiComponentKind/UiVariant from UiRenderingEngine.ts rather than inventing a parallel schema format, so the same <DynamicScreenRenderer> component that paints Matrix Agent's approval screens paints a Compliance Engine permit form too — no new frontend rendering code exists for this engine at all.

In one sentence

Given a country code and a permit type, this engine picks the right jurisdiction's PermitStrategy and returns a ready-to-render form schema — adding a new country is a new strategy class, not a new screen.

How It Works — Resolve, Build, Render

Every schema request follows the same three-stage path from "which country is this jobsite in" to "a working, jurisdiction-correct form on screen":

1. Resolve the Strategy 2. Build the Schema 3. Render with DynamicScreenRenderer
StageWhat happens
1. Resolve the StrategyresolveComplianceStrategy(countryCode) upper-cases the code and switches on it — TR returns TRPermitStrategy, US returns USPermitStrategy, anything else (including an unrecognized or missing code) returns DefaultPermitStrategy. The function never throws.
2. Build the SchemaThe resolved strategy's getPermitFormSchema(permitType) returns a Form component tree — a set of labeled Input fields (some with a regex validation pattern, some numeric with min/max) plus one submit Button.
3. Render with DynamicScreenRendererThe frontend's fetchComplianceSchema() POSTs to /api/compliance-engine/generate-schema and hands the returned UiComponent straight to <DynamicScreenRenderer> — the exact same component the UI Rendering Engine's own approval screens use.

Why reuse UiComponent instead of a bespoke Compliance schema type?

Reusing the UI Rendering Engine's UiComponent/UiComponentKind/UiVariant types means the frontend never needs a second renderer, a second style catalog, or a second set of component-kind handling. A jurisdiction-specific permit form and a Matrix Agent approval screen are structurally the same kind of thing — a small tree of inputs, badges, and buttons — so they share one rendering path end to end.

Input — What a Schema Request Carries

To build a permit form, the engine needs only enough to pick the right jurisdiction and label the request:

Country Code

The two-letter code (TR, US, or any other) that decides which PermitStrategy answers the request. Required on every call — the handler rejects a request missing it.

Permit Type

A free-form string identifying what kind of permit or record this is (e.g. building_permit, zoning_land, legal_liens). Required, but today it only labels the form — it does not change which fields a strategy returns.

Jobsite ID (optional)

Accepted for the audit trail and future use. The caller is responsible for deriving countryCode from the jobsite before calling this engine — passing a jobsiteId does not itself change the schema returned.

permitType doesn't change the field set — yet

The three strategies branch only on countryCode. A Turkish building permit and a Turkish zoning record both currently return the exact same Ada/Parsel/SGK/TAKS/KAKS field set — permitType is threaded through as a label and used to name the generated form (permit_form_tr_${permitType}), but it does not itself branch the schema. This is an honest architectural note about the engine's current scope, not a hidden bug.

Output — A Jurisdiction-Specific Form Schema

The response is a UiComponent Form tree whose fields differ per strategy:

StrategyFields returned
TRPermitStrategyAda, Parsel, Pafta (optional), Müteahhit SGK Sicil No (regex-validated), Şantiye Şefi Belge No (regex-validated), TAKS (numeric, 0–1), KAKS / Emsal (numeric, 0–10), then a submit button.
USPermitStrategyParcel ID, Contractor License Number (regex-validated), OSHA Certification Number (regex-validated), Zoning Classification, then a submit button.
DefaultPermitStrategyA disabled Permit Type field pre-filled with the request's permitType, a required Site Address field, an optional Notes field, then a submit button — the safe generic fallback for any country without a dedicated strategy.

Regex validation travels inside the schema itself

Fields like Müteahhit SGK Sicil No or Contractor License Number carry a regex prop directly on the Input component — the same server-driven-UI convention UiRenderingEngine establishes, so the frontend's shared input renderer already knows how to enforce it without any Compliance-specific validation code.

How End Users Actually Use It

Filing a Permit, Zoning Record, or Legal Contract

Inside the Add/Edit modal on Permit Applications, Zoning & Land Use Rules, or Legal Contracts & Liens (all three in Construction → Permits & Zoning Compliance), a "Generate Form" button calls this engine with the jobsite's resolved jurisdiction and renders the result inline. A Turkish jobsite's user sees Ada/Parsel/SGK fields; a US jobsite's user sees Parcel ID/OSHA fields — without either screen containing a single line of country-specific form code.

Switching Jurisdiction Mid-Record

Each of the three screens carries its own jurisdiction picker (a Globe/ChevronDown-styled SearchableCombobox), pre-filled from the selected jobsite via useJobsiteJurisdiction() but overridable per record. Changing it re-calls this engine and swaps the rendered field set to match — useful for a holding company filing paperwork in a country different from where the jobsite itself is registered.

Where the Compliance Engine Fits

This engine doesn't render anything itself — it builds a schema, and the UI Rendering Engine's renderer is what turns that schema into a screen:

Consumer ScreenWhat it requests
Permit ApplicationspermitType of {category}_permitbuilding_permit, environmental_permit, or scaffolding_permit depending on the category picked in the modal.
Zoning & Land Use RulesA fixed permitType of zoning_land.
Legal Contracts & LiensA fixed permitType of legal_liens.

All three call fetchComplianceSchema() in src/services/complianceEngineService.ts, which reuses (not duplicates) the UiComponent type from uiEngineService.ts — unlike uiEngineService.ts itself, which has to duplicate server-side types since the frontend can't import server code directly. Every one of these three screens' output is painted by the same <DynamicScreenRenderer> component (src/components/app-factory/DynamicScreenRenderer.tsx) that renders the UI Rendering Engine's own approval screens — no new rendering code was written for Compliance Engine's output.

Like every screen the UI Rendering Engine builds, each successful or failed schema generation is logged as a tracked event via the Event & Analytics Engine (engineName: 'ComplianceEngine', eventType: 'compliance-engine.schema.generated' on success, '.failed' on error) — so a jurisdiction that starts failing shows up in the same usage and health history as every other engine call in TotalApp.

A jurisdiction resolver that plugs into an existing renderer, not a new one

This engine's entire job is picking the right PermitStrategy and building its schema. It has no rendering logic, no persistence layer, and no settings screen of its own — it exists specifically so that permit/zoning/legal screens across Construction (and, in principle, any future add-on with country-specific paperwork) can ask "what does this jurisdiction need?" once, in one place.

Frequently Asked Questions

What happens if countryCode is missing or blank?
The HTTP handler (POST /api/compliance-engine/generate-schema) validates that countryCode is present and is a string before building anything — a missing or non-string value returns a 400 error rather than falling through to a default strategy.
What happens for a country with no dedicated strategy, like Germany, the UK, or the UAE?
resolveComplianceStrategy() falls through to DefaultPermitStrategy, which returns a generic permit-type/address/notes form and never throws. These countries are still selectable in every jurisdiction picker in the UI, specifically so a holding company's jobsites in those countries are represented even before a real strategy exists for them.
Does jobsiteId change which fields come back?
No. jobsiteId is accepted and passed through for the audit trail and future use, but the schema is built purely from countryCode and permitType. The caller is responsible for deriving countryCode from the jobsite (via useJobsiteJurisdiction()) before calling this engine.
Does the engine store the submitted permit data itself?
No. Like the UI Rendering Engine, this engine only builds the schema describing the form — it has no persistence layer. The submitted values are stored by the calling screen itself, in PermitRecord.formData, ZoningRegulationRecord.formData, or LegalContractRecord.formData depending on which screen made the request.
Is Compliance Engine specific to the Construction add-on?
Its three current callers are all Construction screens (Permit Applications, Zoning & Land Use Rules, Legal Contracts & Liens), but nothing in the engine itself is Construction-specific — the PermitStrategy pattern and the UiComponent output shape would work equally well for any future add-on that needs country-specific paperwork.
How would a new jurisdiction, like Germany, get real support instead of the default fallback?
By adding a new class implementing PermitStrategy (e.g. DEPermitStrategy) and adding one new case to resolveComplianceStrategy()'s switch statement. No screen that calls this engine needs to change — they already pass whatever countryCode the jobsite carries.