Appendix O — The Inception Cycle (reference)
The full reference for §3.6 The Inception Cycle: the five artifacts, the presence-floor gate, and a worked fitness-function example. Templates live in
templates/(inception-canvas.md,qa-scenario.md,threat-enumeration.md).
When to run it
The Inception Cycle fires for a new project or a new bounded context / major subsystem — a new top-level domain module, a new external-integration class, or a new data-trust boundary (§3.6). Ordinary features never trigger it. It runs once, up front, before the first feature's full cycle; for product-scale (multi-milestone) work it feeds Product-Scale Planning (§5.6) rather than competing with it.
The five artifacts
1. Architecture Inception Canvas (inception-canvas.md)
The arc42 one-pager. Eight blocks: business case, functional overview, ranked quality goals, architecture hypotheses, technical risks, business context, organisational constraints, technical constraints. The point is leanness — it is the floor, not a design document. It transitions to fuller arc42 docs only when the system earns them.
2. Quality-Attribute Scenarios (qa-scenario.md)
At least three prioritized six-part scenarios: source → stimulus → artifact → environment → response → measurable response measure. The measure is the load-bearing part — it is what a fitness function asserts and what later ATAM-style evaluation reuses. A scenario without a measurable response is a wish.
3. Risk-storming (with a security lens)
A collaborative pass over a C4 level-1/level-2 diagram: each participant silently records risks on the diagram near the affected area, then the group reviews — concentrating on risks only one person saw, or where priority is disputed. The security lens is deliberate: it surfaces the trust boundaries that become artifact 4. Risk-storming is the lightweight stand-in for a heavyweight two-session ATAM.
4. Trust-boundary threat enumeration (threat-enumeration.md)
The per-system threat model, required for new data-trust-boundary contexts: where data crosses trust boundaries, and the STRIDE category + mitigation per boundary. Distinct from §15 (which secures the AI collaborator) and from the per-change safety Brainstorm-Gate trigger (§3.1). It answers "what can go wrong at each boundary in this system," once, at inception.
5. Fitness functions (tests/fitness/)
At least one executable check that encodes a quality goal or hypothesis as an automated, commit-time test. This is the one inception artifact that self-enforces — the others are presence-floored, but a fitness function runs in CI and fails when the architecture drifts. It admits as a gate under §7's admission rule (cost ≈ seconds of CI; mechanism = required CI check; retirement = when its scenario is removed or superseded).
The gate is a presence floor
Before the first feature of a new bounded context dispatches: the canvas exists, ≥3 scenarios exist, a risk-storm artifact exists, a threat enumeration exists (for trust-boundary contexts), and ≥1 fitness test exists. Substance is not machine-gated — it rests on the §3.1 second-party-scrutiny threshold, exactly as a design does. The presence floor stops "we skipped architecture entirely"; second-party review stops "we did it perfunctorily." Claiming fail-closed enforcement of substance the content cannot support would be the theater this cycle exists to avoid.
Worked example — a fitness function from a quality scenario
A QA scenario:
QA-2 — tenant isolation [high]: an authenticated user of tenant A submits a query (stimulus) to the data-access layer (artifact) under normal load (environment); the system returns results (response); response measure: zero rows from any tenant ≠ A, asserted on every query path.
The fitness function that pins it (tests/fitness/test_tenant_isolation.py):
# Fitness function for QA-2 (tenant isolation). Architectural invariant, not a
# feature test: it asserts the STRUCTURE holds, and runs as a required CI check.
# Retires when QA-2 is removed/superseded (§7 gate-admission).
import pytest
from app.db import query_paths # every registered data-access entry point
@pytest.mark.fitness
@pytest.mark.parametrize("path", query_paths())
def test_no_cross_tenant_rows(path, seeded_two_tenants):
a, b = seeded_two_tenants
rows = path.run(tenant=a.id, as_user=a.user)
assert all(r.tenant_id == a.id for r in rows), (
f"{path.name} leaked rows from another tenant — QA-2 violated"
)
The test is parametrised over every query path, so a new path that forgets isolation fails the invariant the moment it is added — which is the difference between a fitness function (guards the architecture) and a unit test (guards one behavior).