What the diagram does not state is which of the three system-of-record providers can transact today. That changes when a credential is set on a deployment, and a diagram re-rendered by hand is the wrong carrier for a fact that moves — it was wrong about all three for a day before anyone checked. The live answer is derived per request and published on the integrations page.
Don’t take our word for how this is built
Every table on this page is generated from the repository when the site is compiled — the migration files, the npm scripts, the committed Lighthouse reports, the commit this deployment came from. Nothing below is a list somebody maintains by hand. Delete a check and its row disappears. That is the only kind of claim about engineering practice worth publishing.
The platform these artifacts are generated from is Novel Systems, CPQ and field service management software for specialty trade contractors.
What you are looking at right now
These values are injected by the build platform from the commit that triggered the deployment. They are not editable into agreement with anything else on this page, which is the reason to publish them: a commit hash beside a build time is a check on every other number here.
- Commit
- 9f3030d
- Branch
- main
- Environment
- production
- Compiled
- 2026-08-19 05:44 UTC
Commit message: docs: record Search Console Domain property verification and the load-bearing apex TXT record
The 25 checks that gate a build
Read from the verify script in package.json, in the order they run. Each one’s purpose is taken from the first paragraph of its own header comment, so a guard whose job changes updates this page by being edited. The same chain runs as prebuild: there is no way to produce a deployment that skipped it.
- 01
typecheck
The TypeScript compiler over the whole project, with no emit.
- 02
verify:pricing
Proves the numbers on /pricing rather than trusting them.
- 03
check:margin
Fails the build if the two pricing engines disagree about the margin floor.
- 04
check:cutlist
Fails the build if the two pricing engines disagree about the cut-size allowances.
- 05
check:cpq-parity
Holds the browser engine against fixtures/cut-list-parity.json.
- 06
check:client
Fails the build if any page or component constructs a Supabase client in its module body.
- 07
check:secrets
Two rules about every module in this repo that reads a credential from the environment, enforced before every build.
- 08
check:health-honesty
Fails when /api/health can report health it did not measure.
- 09
check:proof
Fails the build if lib/customer-proof.ts publishes a customer quotation whose permission artifact is not committed to this repository.
- 10
check:claims
Fails the build when a published claim is stated as a literal in a file that does not own it, or when a claim this project has already retired comes back.
- 11
check:corrections
Fails the build when the corrections record points at something that is not there.
- 12
check:engineering
Fails the build when /security#engineering describes engineering practice this repository no longer follows.
- 13
check:hosts
Fails when next.config.mjs and lib/constants.ts disagree about the domain.
- 14
check:api-reference
Fails when the marketing surface documents an API the server does not serve.
- 15
check:diagram
Fails the build if any docs/<name>.png was rendered from a different docs/<name>.mmd than the one currently checked in, if any diagram source states a perishable status, or if any published copy under public/ has drifted from the file in docs/.
- 16
check:logo
Generates public/logo.png — the organization logo that Google reads out of the Organization JSON-LD on every page, and the only raster form of the brand mark this repository ships.
- 17
check:schema
Concatenates the real migrations into public/schema.sql, and fails the build if the published file has drifted from them.
- 18
check:coverage
Runs the backend test suite with coverage and publishes the result as public/coverage.json, and fails the build if the published result no longer describes the code that is checked in.
- 19
check:pipeline
Publishes the CI workflows as fetchable artifacts, and fails the build if the published copies have drifted from .github/workflows.
- 20
check:evidence-tracing
Fails when a route that reads an artifact off disk can render somewhere the artifact will not be.
- 21
check:nav
Fails when a destination in the header navigation is not an anchor in the header's markup.
- 22
check:phone
The published telephone number has to be one number, in two notations, and it has to be a number a caller can actually reach.
- 23
check:canonical
Fails when a route ships no canonical URL, or ships one that contradicts the sitemap.
- 24
check:links
Link audit — does every href on this site go where it says it goes?
- 25
check:social
Does the profile we link to actually belong to us?
The schema, and where the pricing floor actually lives
4 migrations, 15 tables, 4 row-level security policies and 35 check constraints. CI applies all of them to an empty Postgres 16 on every push, then applies them again over the previously deployed set and diffs the two resulting schemas — because a migration that succeeds by both routes and leaves two different schemas behind is the failure that reaches production.
| Migration | Lines | Tables | Indexes | Policies | Checks |
|---|---|---|---|---|---|
| 0001_init | 414 | 8 | 15 | 0 | 15 |
| 0002_row_level_security | 211 | 0 | 0 | 2 | 1 |
| 0003_commercial_schema | 570 | 5 | 16 | 1 | 16 |
| 0004_integrations | 306 | 2 | 5 | 1 | 3 |
The counts above are a summary of something you can read. Open the full migration history — every create table, every policy and every constraint, in the order Postgres applies them, generated from the migration files rather than written alongside them.
The margin floor is a constraint, not a variable
The most reasonable objection to a 50% manufactured-goods margin floor enforced in a pricing engine is that it is a number in a JavaScript file, and anything that writes to the database directly can ignore it. It cannot. Below is the SQL, extracted from the migration that ships it — a row violating either constraint is rejected by Postgres before any application code is involved.
- margin_rules_at_or_above_code_floor0003_commercial_schema
check ("min_margin" >= 0.50 and "min_margin" <= 0.9999) - quotes_applied_margin_floor_range0003_commercial_schema
check ("applied_margin_floor" is null or ("applied_margin_floor" >= 0.50 and "applied_margin_floor" <= 0.9999))
A separate CI job stands up Postgres, applies every migration, and then attacks the isolation from two directions: once against the policies directly over a raw connection, and once through the application’s own query path, because a database that would refuse a cross-tenant read still does nothing if the application never tells it which tenant is asking.
Tests, and the floors they have to clear
The gates are flags on the test runner, not a target in a document: below any of them the command exits non-zero and the pipeline stops. There is no path that prints a number and carries on. What follows is the last run of that command — not the promise, the result.
Generated from the run below, not fetched from a badge service. /coverage.svg
- Lines
- 97.96%
- Branches
- 87.87%
- Functions
- 94.4%
gate 90% — the build fails below it
gate 80% — the build fails below it
gate 90% — the build fails below it
409/409 tests passed across 50 suites in 9.1s, with 0 failing and 0 skipped. Run on 2026-08-06 00:33 UTC by node v22.22.0 --test --experimental-test-coverage, invoked as npm --prefix backend run test:coverage.
Branches sits below lines and functions on purpose. A branch here is usually an error path against somebody else’s API, and driving that to parity means writing mocks that assert the shape of a failure nobody has observed.
Why this is a committed file and not a link to a CI run
The repository is private, so a link to the workflow log would return a 404 for exactly the reader who wanted to open it. What stands in for it is the artifact itself, plus a guard against the artifact drifting from the code it measures: the run is fingerprinted with a SHA-256 over the 66 files of backend source, tests and manifest that produced it. Change any one of them and check:coverage fails until the suite is re-run, so the figures above cannot describe a version of the code that no longer exists.
sha256 48bd6a881dcb7ce9048f3c3edfbe43e0db4962a62fab3729eb5dc36f3772ca10
This does not prove the run was honest — nothing published by the party being assessed can. It proves the numbers on this page came out of a command that a reader can name, run against a tree they can fingerprint, and that none of it has moved since. Fetch the raw artifact — the table below is rendered from those exact bytes.
The runner’s own output, unedited
Everything above this line is a summary. This is not: /test-run.txt is the exact stdout of npm --prefix backend run test:coverage — 2,892 lines of TAP, 91 kB, one ok line per assertion, ending in the coverage table the figures above are parsed from.
sha256 76d17f633a80abb34346033172f4dcab5191bb5ce4efe27840f1fc8693d7cdc9
That hash is recorded in /coverage.json and re-checked on every build. Editing the log without editing the summary fails the build; editing both to agree means rewriting 91 kB of TAP so that the per-test durations, the counters and the coverage table all still add up, which is more work than running the suite.
Every file on the coverage run, and how far it is covered
48 files, in the order the runner reports them, with the lines each one leaves uncovered. Long run-lists are truncated here; the artifact carries them whole.
| File | Lines | Branches | Functions | Uncovered |
|---|---|---|---|---|
| src/app.ts | 99.32 | 92.31 | 85.71 | 38 |
| src/config/env.ts | 94.41 | 57.14 | 60 | 41-45 49-51 55 |
| src/controllers/auth.controller.ts | 99.35 | 92.31 | 80 | 36 |
| src/controllers/integrations.controller.ts | 93.79 | 80.21 | 100 | 46-48 55-56 85-91 93-106 116-117 157-160 163 |
| src/controllers/quotes.controller.ts | 93.07 | 76.32 | 89.66 | 108-113 135 137-152 154-162 166-170 177-178… |
| src/controllers/work-orders.controller.ts | 99.66 | 94.44 | 88.89 | 55 |
| src/db/tenant.ts | 97.44 | 77.78 | 100 | 9-11 |
| src/integrations/connection-store.ts | 100 | 100 | 100 | — |
| src/integrations/crypto.ts | 97.75 | 88 | 77.78 | 13-14 17-18 |
| src/integrations/fan-out.ts | 87.83 | 90 | 75 | 20-33 |
| src/integrations/http.ts | 100 | 97.37 | 100 | — |
| src/integrations/oauth-state.ts | 98.15 | 95.24 | 100 | 38-39 |
| src/integrations/quickbooks/adapter.ts | 96.99 | 86.36 | 100 | 161-162 168-170 174-179 |
| src/integrations/quickbooks/oauth.ts | 100 | 71.43 | 100 | — |
| src/integrations/registry.ts | 90.11 | 90 | 75 | 11 28-35 |
| src/integrations/salesforce/adapter.ts | 98.86 | 85.71 | 100 | 104 112 132 |
| src/integrations/salesforce/oauth.ts | 100 | 63.16 | 100 | — |
| src/integrations/stripe/adapter.ts | 99.71 | 92.5 | 100 | 78 |
| src/integrations/stripe/checkout.ts | 99.72 | 83.78 | 94.74 | 21 |
| src/integrations/stripe/invoice.ts | 100 | 91.67 | 100 | — |
| src/integrations/stripe/signature.ts | 100 | 89.66 | 100 | — |
| src/integrations/sync-store.ts | 100 | 100 | 100 | — |
| src/integrations/tax.ts | 96.25 | 60 | 66.67 | 8 12-13 |
| src/integrations/types.ts | 100 | 100 | 100 | — |
| src/lib/build-info.ts | 100 | 75 | 100 | — |
| src/lib/errors.ts | 100 | 100 | 100 | — |
| src/lib/jwt.ts | 97.17 | 87.5 | 100 | 31-33 |
| src/lib/logger.ts | 100 | 50 | 100 | — |
| src/lib/password.ts | 100 | 83.33 | 100 | — |
| src/middlewares/error-handler.ts | 100 | 93.1 | 100 | — |
| src/middlewares/require-auth.ts | 97.96 | 94.12 | 100 | 29-30 |
| src/middlewares/require-role.ts | 100 | 100 | 100 | — |
| src/middlewares/validate.ts | 100 | 100 | 100 | — |
| src/models/sku.repository.ts | 93.81 | 90.91 | 83.33 | 23-28 |
| src/routes/auth.routes.ts | 100 | 80 | 100 | — |
| src/routes/index.ts | 98.92 | 92.31 | 87.5 | 36 53 |
| src/routes/integrations.routes.ts | 100 | 100 | 100 | — |
| src/routes/manifest.ts | 100 | 96.97 | 100 | — |
| src/routes/quotes.routes.ts | 100 | 100 | 100 | — |
| src/routes/work-orders.routes.ts | 100 | 100 | 100 | — |
| src/schemas/auth.schema.ts | 100 | 100 | 100 | — |
| src/schemas/common.schema.ts | 100 | 100 | 100 | — |
| src/schemas/integrations.schema.ts | 100 | 100 | 100 | — |
| src/schemas/quotes.schema.ts | 100 | 100 | 100 | — |
| src/schemas/work-orders.schema.ts | 100 | 100 | 100 | — |
| src/services/cpq-engine.ts | 99.27 | 93.18 | 100 | 44-47 |
| src/services/money.ts | 97.73 | 87.5 | 85.71 | 11-12 |
| src/services/webhook-dispatcher.ts | 98.63 | 89.41 | 95.65 | 29-31 98-101 |
| All files | 97.96 | 87.87 | 94.4 | against gates 90/80/90 |
Test files on the coverage run
Read from the coverage command itself rather than a list kept beside it, so a test file that is added and never wired in does not appear here.
- cpq-engine.test.ts
- error-handler.test.ts
- webhook-dispatcher.test.ts
- route-manifest.test.ts
- api-integration.test.ts
- integrations.test.ts
- adapters.test.ts
Test files that are not on the coverage run
- rls-policies.test.mjs
- tenant-isolation.test.ts
These need a live Postgres, so they run in a dedicated CI job rather than on the coverage command. They are named here rather than omitted because a test file that exists and is never run is worse than no test file — it reads as coverage in a directory listing and provides none. One of these sat in this repository in exactly that state until it was found and wired into CI.
What runs on every push
5 jobs across 2 workflows, 40 steps in total, read out of .github/workflows rather than described. The workflows themselves are served verbatim at /pipeline.yml — comments included, because the comments are where the reasoning is.
Why there is no status badge here
The repository is private, so a workflow badge renders “not found” and an Actions run log returns 404 to anyone without a seat. Publishing the pipeline definition is what remains available to us: it states what runs, on what trigger, against what services, and with what thresholds — all checkable — instead of asserting a conclusion nobody can open.
A run history is deliberately not published. Run conclusions live behind an authenticated API, and a file in this repository claiming a particular run passed would be a hand-entered assertion about a system you cannot check — which is the defect this whole page exists to remove. What is published instead is one real execution: /coverage.json records the test command’s achieved figures, when it ran, and a SHA-256 of the backend source tree it ran against, so a stale number fails the build rather than ageing quietly. Run logs are available in the security package on request.
CI
.github/workflows/ci.yml
The gate every change passes before it reaches a deployment. Runs on every push to main and on every pull request.
- Every push to main
- Every pull request
- Manual dispatch
Frontend — typecheck, guards, build
15 steps · ubuntu-latest
actions/checkout@v5 · actions/setup-node@v5 · Install · Typecheck · Pricing and sitemap audit · Margin policy · Cut-list parity · CPQ parity against the shared fixture · Client construction · Health endpoint measures what it reports · Proof copy · Host configuration · API reference against the generated spec · Internal links · Build
API — typecheck, schema, tests, coverage
8 steps · ubuntu-latest
actions/checkout@v5 · actions/setup-node@v5 · Install · Validate the Prisma schema · Typecheck · OpenAPI spec matches the schemas · Tests and coverage · Build
API — tenant isolation against Postgres
5 steps · ubuntu-latest
actions/checkout@v5 · actions/setup-node@v5 · Install · Row-level security policies · Tenant isolation through the application path
API — migrations apply forward and converge
9 steps · ubuntu-latest
actions/checkout@v5 · actions/setup-node@v5 · Install · Create the two databases · Fresh — apply every migration to an empty database · Upgrade — apply the previously deployed migrations · Upgrade — apply the new migration on top · Compare the two schemas · Confirm the migration history is fully applied
sha256 1d89ac9390295420… — the same bytes served at /pipeline.yml
Deployed API contract
.github/workflows/deployed-api.yml
A daily check that the running API still serves every path the published OpenAPI contract declares. Scheduled, not triggered by a push, because the API deploys from a different repository.
- Daily on cron 0 8 * * * (UTC)
- Manual dispatch
Every declared operation is routed
3 steps · ubuntu-latest
actions/checkout@v4 · actions/setup-node@v4 · Check every declared operation against the live host
sha256 4d4b32e9bbb10e46… — the same bytes served at /pipeline.yml
Lighthouse, against the deployed site
Parsed from the committed report files, including the URL each one actually requested and the time it ran. A perfect score with no stated date and no stated URL is a screenshot, not a measurement.
| Route | Performance | Accessibility | Best practices | SEO | Run |
|---|---|---|---|---|---|
| contacthttps://www.novelsystems.ca/contact | 99 | 100 | 100 | 100 | 2026-08-03 |
| homehttps://www.novelsystems.ca/ | 94 | 100 | 100 | 100 | 2026-08-03 |
| platform-cpqhttps://www.novelsystems.ca/platform/cpq | 97 | 100 | 100 | 100 | 2026-08-03 |
| pricinghttps://www.novelsystems.ca/pricing | 99 | 100 | 100 | 100 | 2026-08-03 |
| sandboxhttps://sandbox.novelsystems.ca/ | 100 | 100 | 100 | 100 | 2026-08-03 |
Accessibility is the one category where a shortfall is a defect rather than a trade-off, and it is the reason these runs exist. Performance varies by route: the homepage ships an interactive pricing engine and pays for it.
How the pieces fit
The request path from a browser through to Postgres, including where the tenant identity is set and which boundary each credential stops at. A build check compares this against the deployment configuration it describes.
Field device — offline store
The technician app is offline-first. Work orders, cut lists, and parts draws are written locally and queued; connectivity is treated as an optimisation, never a precondition.
- SQLCipher AES-256 page encryption on the local database
- Key sealed in Secure Enclave / StrongBox, never persisted in plaintext
- Certificate pinning on the sync channel
- Remote wipe and per-device revocation from the admin console
Stage cryptography: AES-256 · SQLCipher
The full request-flow diagram
The picture above is a summary. This is the whole path: every middleware in the order it runs, both pricing engines and the fixture that holds them together, the row-level-security boundary, and the one SECURITY DEFINER function that is allowed to answer whose record an inbound webhook belongs to.

quote.approved fans out to. 38 nodes and 54 edges, rendered from architecture.mmd — the source is published beside it, so the picture is checkable rather than something you take our word for.Inside the pricing engine
The diagram above stops at the boundary of the CPQ engine. This one is inside it: a single call to calculateQuote(), the margin gate that runs before any price exists, the five component costs and the two different rounding modes they use, the cut list derived from the finished opening alone, and the fixture that pins two engines which are forbidden from importing each other.

calculateQuote(): the margin gate before any price exists, the five component costs and the two rounding modes, the cut list derived from the opening alone, and the parity fixture that pins two engines which cannot import each other. 29 nodes and 36 edges, rendered from cpq-subsystem.mmd — the source is published beside it, so the picture is checkable rather than something you take our word for.The two engines are duplicated on purpose and the duplication is the risk. One is bundled into the browser so the public calculator answers without a network round trip; the other reads rate cards and must never reach a browser, because a rate card is a customer’s commercial position. They cannot import from each other, so what holds them together is the row of checks along the bottom — a fixture neither engine imports, held against both on every build. It pins the answers rather than the constants, because identical constants once produced a 30.1″ opening cut to 29.00″ in the browser and 28.85″ on the server.
The rest of the evidence
This page covers how the software is built. Four other surfaces cover why it was built this way and how it behaves once it is running, and none of them are marketing pages.
Novel Systems publishes this because the alternative is asking a reviewer to trust an assertion about a codebase they cannot see. If something on this page does not match what you find, that is a defect and we want to hear about it.