feature:xray
Table of contents
- Introduction
- Reading the map
- Looking at one feature or one kind of thing
- Checking for collisions
- Stale contracts
- Reference
- Requirements
- See also
Introduction
feature:xray shows how the features of your product fit together, read from their contracts. You see which layout slots each feature fills, what each one exposes, and what refiners override or extend. Run it when you add a refiner or a hook, or to find out who changes what in a product you did not write.
splent feature:xray
Feature X-Ray — my_first_app
my_first_app (product layout)
template: base_template.html
hook: layout.anonymous_sidebar ← splent_feature_notes
hook: layout.authenticated_sidebar ← splent_feature_notes, splent_feature_reports
3 unused: layout.head, layout.scripts, layout.modals
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
splent_feature_notes_tags (refines splent_feature_notes)
service: NotesService → NotesServiceWithTags
model: Notes + NotesTagsMixin
splent_feature_reports, splent_feature_landing — no active refinements
2 refinement(s) across 1 feature(s).
Reading the map
The product layout comes first. Its slots come from [tool.splent.layout] in your product’s pyproject.toml (base_template and hooks). Each filled slot lists the features that fill it, in load order, and the empty ones are counted as unused.
Then comes one section per feature.
| Feature | Its section lists |
|---|---|
Declares [tool.splent.contract.extensible] |
The points in use, overridden or extended by a refiner, or hooks another feature fills |
| Declares no extension points | What its contract provides |
| A refiner | What it overrides (→) or extends (+) |
| Nothing to show | Collected on one line at the end |
This is the static view of what the RefinementRegistry applies when the app starts.
Looking at one feature or one kind of thing
splent feature:xray splent_feature_notes
splent feature:xray --hooks
splent feature:xray splent_feature_notes --services
Name a feature to see only its section. A filter shows one category, --services, --templates, --models, --routes, --hooks, --commands or --signals. The filters share one setting, so only the last one you give applies.
To see every extension point and every unused layout slot, not only the active ones, add --full. Idle points are marked (extensible).
Checking for collisions
splent feature:xray --validate
── Validation ──
✅ All 10 checks passed. (1 shared hook(s), 3 unused layout hook(s))
--validate runs the checks below over every feature of the product. Each failure is one line, followed by <n> issue(s) found, <m> passed., and the command exits 1.
❌ Model 'Notes' — collision: splent_feature_notes, splent_feature_dashboard
| Check | Fails when |
|---|---|
| Services | A service class is in two contracts and no refinement overrides it |
| Models | The same model class is in two features |
| Routes | The same route is in two features |
| Blueprints | The same blueprint name is in two features |
| Commands | A command is listed twice within one feature. Feature commands are namespaced, so two features never collide |
| Signals | Two features provide the same signal |
| Hooks | Never. Shared slots are counted, since hooks add up |
| Layout hooks | Never. Unused slots are counted |
| Refinement targets | A refiner overrides or extends a point its base does not declare extensible, or adds routes when the base lacks routes = true. One check per refinement entry |
Stale contracts
xray trusts the contracts, so it checks the editable ones first. When a feature’s source is newer than its pyproject.toml, it lists it and asks Update stale contracts now?. Yes refreshes them as feature:contract --write does.
With no answer to read, as in CI, it prints Run: splent feature:contract <feature> --write and goes on with the contracts as they are. Pinned features are never checked.
Reference
| Argument or option | Description |
|---|---|
FEATURE_REF |
Only this feature. Without it, every feature of the product |
--services |
Service classes and their overrides |
--templates |
Template files and their overrides |
--models |
Model classes and their extensions |
--routes |
Routes and the routes refiners add |
--hooks |
Hook registrations and the slots they fill |
--commands |
The commands the feature contributes |
--signals |
The signals the feature provides |
--validate |
Run the collision checks |
--full |
Every extension point and every unused layout slot |
xray reads [tool.splent].features plus the list for SPLENT_ENV (dev when unset). A pinned entry is read from its features/<namespace>/<name>@<version> link or that exact snapshot, an unversioned one from the workspace root.
| Message | What to do |
|---|---|
Feature '<name>' not found in product. |
Use a name the product declares. Exits 1 |
pyproject.toml not found. |
Select a product that exists. Exits 1 |
Requirements
- A selected product (
SPLENT_APP).
See also
feature:hookslists the hook registrations of one feature.feature:contractinfers and updates a contract.feature:compatcompares two features.- Template system explains how hooks work at runtime.
- Extensibility covers extension points and refinements.