Skip to main content

The Substrate Line

The Doctrine commits Helm to per-shop reprogrammability. Without a clear line between what reprograms and what doesn't, "reprogrammable" becomes "anything goes" and the platform dissolves into a thousand forks.

This page draws the line.

What Is Canonical

The substrate. Identical at every shop. Never drifts. Only Kvick modifies it.

  • Data model. Table structure, column types, foreign keys, soft-delete semantics, cents-as-integers. The shape of stored truth.
  • Audit chain. The eight-table tamper-evident hash chain. Append-only. No exceptions.
  • Regulatory layer. Tax codes, PST/GST handling, jurisdictional rules, anything tied to law. Changes are pushed by Kvick when the law changes.
  • API surface. The contract between the front-end and the data. New endpoints can be added; existing endpoints can be extended but not broken.
  • Security boundary. Authentication, authorization, role gates, the audit-write rules. Single source.
  • Identity model. Who counts as staff, customer, vendor; the rules for merging or deleting them.

If a per-shop change would alter any of the above, it does not happen at the shop. It happens at Kvick, in the substrate, and propagates.

What Is Open

The surface. Free to drift per shop.

  • Workflows. Ticket lifecycles, approval steps, who-does-what-when.
  • UI. Layouts, colors, screen organization, surface affordance positions if a shop insists.
  • Modules. Optional features turned on or off; entirely new modules built for one shop.
  • Reports. Anything aggregating the canonical data into shop-specific views.
  • Integrations. Per-shop connectors to tools the substrate doesn't know about.
  • Language. Field labels, button text, what the shop calls things.

Drift here is desired. Drift here is the product.

The Test

When a request arrives — from a walkthrough, a feature ask, a Claude Code session — it sits on one side of the line or the other. The test:

Would a change here, made at one shop, become unsafe or incorrect if not made at every shop?

If yes — substrate. Kvick decides. Propagate.

If no — surface. The shop decides. Ship.

Examples:

RequestSideWhy
BC PST rate changesSubstrateEvery shop must apply it or the books are wrong
New work-order column for SwickedSurfaceSwicked's column doesn't have to exist anywhere else
New field on the customers tableSubstrateChanges the data model — propagates by definition
New sort order on the customer listSurfacePure UI decision
Per-shop role permissions configSurfaceReads the canonical role gates without changing them
A new role gate itselfSubstrateChanges the security model

Why This Line Matters

Without it, two failure modes:

Substrate drift. One shop's "small change" alters the data model. Three years later, that shop's database cannot accept a regulatory update without a custom migration. Multiply by a hundred shops. Kvick becomes a maintenance company servicing a hundred broken forks. Service multiples, not platform multiples.

Surface paralysis. Without explicit permission for drift, every change gets escalated as a substrate decision. The shop waits. The Doctrine is broken. PSaaS becomes regular SaaS with extra steps.

The line protects both directions. It is the load-bearing wall of the platform.

The bike vertical sorted: what's portable, what's tissue

The Substrate Line above is abstract. Applied to a concrete L1 (the bike vertical's code), the test sorts capabilities into three buckets — and the third is the interesting one.

L1 capabilityClassificationWhy
Identity, PINs, role gatesPortable coreEvery retail business has staff and access control
Audit chainPortable coreTamper-evidence is industry-independent
Customers (people + orgs)Portable coreEvery business has customers
Money / transactions / refunds / tax enginePortable coreCommercial arithmetic is universal; BC tax is regulatory substrate
Inventory / SKU / variantPortable core (mostly)Any goods business stocks things; bike attributes are tissue
Purchase orders / receiving / vendorsPortable coreAny business that buys stock
Bikes-on-recordVertical tissueA serial-numbered customer asset is specific to bikes
Trade-in flowVertical tissueUsed-bike-as-partial-payment is a bike-shop workflow
Service kanban (Dropped Off → … → Picked Up)Vertical tissue, pattern portableThe columns are bike-specific; "intake → work → ready → collected" generalises
Rental fleet / bookingsVertical tissueSpecific to shops that rent

The third column — patterns that generalise

The interesting cases are the ones where the implementation is tissue but the pattern is portable. The service kanban is the canonical example: the four bike-specific column names mean nothing to a plumber, but "intake → work → ready → collected" is the universal shape of any trade where a customer drops something off, you work on it, you notify them it's ready, and they pick it up.

When you harvest L1 for a new vertical (Scenario 2), the rule is:

  • Portable core → harvest verbatim with concept_keys intact
  • Vertical tissue (pattern portable) → repurpose the shape under a new name appropriate to the new vertical
  • Vertical tissue (pattern doesn't generalise) → retire — rentals for a trade that doesn't rent, trade-in for a trade with no used market

This sort is the spec for what a fork of the bike vertical keeps, renames, and drops. The concept_key of every harvested L1 row is what lets the new vertical's L2 canon line up against it later.

Where The Line Lives

Encoded in this bible. Every substrate table, audit rule, regulatory module, and API contract is documented in its own chapter. Anything not documented as substrate is, by default, surface.

When in doubt, the substrate page wins. When the substrate page is silent, the change is surface.

See also