Validation guardrails

Table of contents

Introduction

The framework checks refinement declarations, and the collisions between refiners, every time your app starts. The CLI runs the same rules earlier, so an invalid or colliding refinement never reaches a running product.

splent product:validate --only refinement
  4. Refinement  (refiners, splent product:validate --only refinement)
     ✅ No collisions between the refiners of this product (1 refiner)

Where the rules run

The same refinement rules run in the wizard, in product:validate and at boot splent_framework.refinement.conflicts find_conflicts, the collision rules feature:refine while you configure a refiner, taken targets cannot be chosen product:validate phase 4, before boot, reads the source, imports nothing The boot FeatureError before any mixin is applied
The boot reads the live classes, product:validate the same facts statically, so both reach the same verdict.
Where When On a collision
feature:refine While you configure a refiner A taken target is shown as taken, and a refiner is not offered as a base
product:validate Before boot, and before product:derive and product:build Phase 4 fails the product
The boot When the product starts One FeatureError lists every collision, and the app does not start

What a declaration must respect

Each refiner may only touch what its base declares extensible in [tool.splent.contract.extensible]. If a rule fails, the app stops with Refinement validation failed: and every error.

Rule Error
The base is in the product <refiner>: refines 'shop' but it is not declared in the product.
An overridden service, template or hook is extensible <refiner>: overrides service 'X' from shop, but shop does not declare it as extensible.
An extended model is extensible <refiner>: extends model 'X' from shop, but shop does not declare it as extensible.
Added routes target a base with routes = true <refiner>: adds routes to blueprint 'x_bp' from shop, but shop does not declare routes as extensible.

Collisions between refiners

Once the declarations are valid, and before any feature loads, the framework compares what every refiner declares and brings.

The refiners of this product collide, so it cannot start.
  - splent_feature_collections and splent_feature_sales both add collection_id to the model Item of splent_feature_shop. Rename it in one of the mixins, or declare them incompatible in the UVL with !(collections & sales).
Code Collision Fix
chain A refiner refines another refiner Refine the original base
duplicate-override Two refiners override the same template or service, or the same slot of one base Keep it in one, or !(a & b) in the UVL
attribute-twice Two refiners add the same attribute, or the same table column, to one model Rename it in one mixin, or !(a & b)
shadows-base A refiner adds an attribute its base model defines Rename it, or mark a method with @typing.override
undeclared-template A refiner ships a template its base ships, without declaring it Declare it, or rename the file
missing-template A refiner declares a template override it does not ship Add the file, or drop the declaration
duplicate-route An added route repeats a URL for the same method Give the route another URL

The same checks run again as each piece is applied, so an undeclared refine_service or replace_template_hook call stops startup with the same wording.


Checking before boot

Phase 4 of product:validate runs every rule without importing any feature. It reads declarations from each pyproject.toml, mixin attributes from the source, and templates from templates/. It also checks the whole SPL, and warns about each pair of colliding refiners the UVL allows together.

     ⚠️  The UVL model allows collections and sales in one product, and they collide. Add !(collections & sales) to its constraints.

feature:xray --validate runs the declaration rules without Flask, but not the collision rules.

Reading the source assumes every branch of an if in a class body runs, and that an unknown decorator keeps the @typing.override mark. The boot still checks the live classes.


Seeing who refines what

splent feature:xray
  splent_feature_notes (refined by splent_feature_notes_tags)
       service: NotesService  ← overridden by splent_feature_notes_tags
         model: Notes  ← extended by splent_feature_notes_tags
          hook: notes.create.form_extra  ← splent_feature_notes_tags

--full shows every extensible point, not only the ones in use.


See also


Back to top

splent. Distributed by an LGPL license v3. Contact us: drorganvidez@us.es