Appendix B - 13. Journey-criticality → test-tier placement (which host a test runs on, derived)
The Structure of Journey-criticality → test-tier placement (which host a test runs on, derived) — its shape at a glance:
Each journey-part's criticality derives its host tier by a total function, stored nowhere by hand. A coverage-floor lint walks the model and fails when a major part has no test in a local home.
flowchart TD
Part[Journey-part<br/>criticality] --> Derive{major?}
Derive -->|yes| Local[local + staging]
Derive -->|no| Staging[staging only]
Local --> Floor{{Coverage-floor lint}}
Floor -->|major part, no local test| Fail([finding])
Accessible description: a journey-part's criticality routes through a derivation — a major part to local plus staging, a minor part to staging only. A coverage-floor lint checks that every major part has a local test and reports one that does not.
Projected from the catalogue entry system-models / Journey-criticality → test-tier placement (which host a test runs on, derived).
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — Make a journey's criticality the single input that derives which environment tier its tests run in, and hold a coverage floor over the derivation: every high-criticality journey-part carries a test in the fast local tier, so a green local run means the major paths work. Placement is derived from a typed trait, never hand-drawn (our instance: a typed journey-criticality model whose MAJOR/MINOR axis derives each journey-part's local vs staging tier, guarded by a coverage-floor lint).
Motivation
A test suite runs across environments of different cost. A fast local tier gates every commit; a heavier staging tier runs the full matrix. Which test runs where is usually a hand-drawn line — "keep the important journeys local, push the heavy remainder to staging" — and a hand-drawn line drifts from what actually matters. So a major user path ends up staging-only: the local gate passes, a reviewer merges, and the broken revenue-core flow surfaces only later on staging. Call it the local-green / staging-red surprise. It has two causes, and both come from the line being drawn by hand:
- A major path has no local test at all. Nothing ran it in the fast tier, so local-green says nothing about it.
- The tier was mis-assigned to speed up the local run. Someone shoved a major path staging-only as a local-time optimization, and the criticality signal never objected.
The failure is a fast gate that is trusted to mean more than it covers: local passes, the majors look green, and one of them is broken.
Applicability
- A typed journey model with a criticality field and addressable parts. The derivation attaches to parts; without part-granularity the placement is too coarse.
- A tier taxonomy with a total derivation. Every criticality level must map to exactly one tier, or the function is partial and a new level falls through silently.
- A sanctioned set of local test homes and a join key from a journey to the tests that exercise it, so the coverage lint can decide "does this major part have a local test?" deterministically.
- Environments with a real cost gradient. The mechanism earns its keep only when the tiers differ in cost enough that placement matters; one uniform test run needs no placement model.
Structure
The Structure diagram appears at the top of this page.
Sample Code
The tier is a pure function of criticality — a stored tier literal is banned, so demoting a part to staging-only forces a visible criticality edit. The coverage-floor lint fails when a major part has no local test, making "local-green ⟹ every major path ran locally" a checked property.
import sys
def derive_tier(criticality: str) -> str:
"""Total function: criticality alone decides the host tier. No hand-typed tier literal."""
return "local-depth" if criticality == "major" else "staging-full"
def coverage_floor(parts: list[dict], local_tests: set[str]) -> list[str]:
"""Every major journey-part must have a test in a sanctioned local home."""
findings = []
for p in parts:
if p["criticality"] == "major" and p["join_key"] not in local_tests:
findings.append(f"major part '{p['name']}' has no local test")
return findings
if __name__ == "__main__":
# `load_parts` reads journey-parts + criticality; `local_test_keys` lists tests in local homes.
findings = coverage_floor(load_parts(), local_test_keys())
for f in findings:
print(f"UNCOVERED-MAJOR: {f}")
sys.exit(1 if findings else 0)
Consequences
- The floor closes absence, not quality. The lint proves a major part has a local test; it cannot prove that test drives the real gesture rather than a hollow stub. That residual stays human review.
- The join key is load-bearing. A major part with no join key to its tests cannot be checked, so it is itself a finding — which adds a small annotation cost to every journey.
- Reclassification is the only escape, by design. Moving a part off the local floor forces a criticality demotion — friction that is the point, but friction nonetheless. A team that over-marks major will feel the local tier grow.
- Two levels may be too few, or too many. The coarse major/minor cut is right-sized for a small product; a large one may need a third tier, which means extending the derivation, not hand-placing the outliers.
Example use within DocAble
- A typed journey-criticality model whose
MAJOR/MINORaxis derives each journey-part'slocal-depthvsstaging-fulltier at load, storing the tier nowhere by hand. - The coverage-floor lint: every major journey-part must map to a
@journey-tagged real-gesture spec in a sanctioned local test home, or it is a finding (landed audit-only first, since the composed editor-remediate journey has a known gap). - The per-context selector: a pure function from a deploy context to a frozen set of typed test-specs, reading the model to emit the local floor, the full staging matrix, or the light production smoke. The cluster-deploy driver imports it and gates each phase on membership in the selected set, in place of the per-environment guards, so the selector drives which phases the live deploy runs. Each spec's context set is derived, always including staging, so the staging-covers-each containment is a property the accompanying test suite and a live-model lint check by calling the function — not by auditing a deploy matrix.
Related Patterns
- Sibling derivation — formal-invariant-verification: both derive a verification tier from a typed trait — there the invariant's temporal shape routes its checker, here the journey's criticality routes its host. Same derive-a-tier-from-a-model-trait mechanism, different trait and different tier axis.
- Counterpart — coverage-model-mapping: that asks which model invariants have any covering test (coverage presence over invariant nodes); this derives which host tier a test runs on from a criticality trait, and holds a floor that the fast tier covers the majors. Test-adequacy from two angles: is the node tested at all, versus does the cheap gate cover what matters.
- Enabler — executable-source-of-truth: the criticality axis and the derived tiers are fields on the typed model; this is one more consumer of that substrate, and drift-parity-gates keep the model's journeys matching the real specs the lint reads.
- Ground truth — the user-journey model supplies the journeys and the criticality designation this consumes; the service-flow model carries the typed journey entities whose steps anchor the parts. This mechanism points that criticality cut at a new output: test-host placement.