# Architecture fitness functions A fitness function is a test of a **structural** property rather than a behavioural one. "The domain package imports no infrastructure" is not something you can check by calling the system; you check it by reading the imports. Once it is a test, the property survives the next person who is in a hurry. Lesson 0022 argues for them; `tools/validate.py` at the repository root is one the course applies to itself. This directory is the same idea applied to `returns_lab`. ## Why most of them are red Each fitness function here describes the system you are trying to build, not the one you have. So most fail today, and each is marked: ```python @checkpoint("phase-1 / exercise 1.7") def test_domain_modules_import_no_infrastructure_module(self): ... ``` `@checkpoint(label)` is `unittest.expectedFailure` plus the label of the exercise that turns it green. A red fitness function that is *expected* to be red does not fail the build; it sits in the report as a debt you have named. ## Working one 1. Open `lab/checkpoints/phase-N.md` and find the exercise the label names. 2. Delete the `@checkpoint(...)` line above the test. 3. Run it. It fails. Now make it pass. 4. Run the whole suite — `python3 run_tests.py` from `lab/` — and check that no characterization test in `tests/` went red on the way. If you fix the code but leave the decorator in place, the runner reports an **unexpected success** and the suite goes red. That is deliberate: a fitness function still marked as failing after you fixed it is a lie about the architecture. ## What is in here | File | Property under test | Phases | |---|---|---| | `test_fitness_dependency_direction.py` | The arrows point at the domain; nothing has a fan-out of more than five siblings. | 1, 2 | | `test_fitness_vendor_names.py` | No vendor names a domain concept; upstream payloads are translated at the edge. | 1, 2, 3 | | `test_fitness_global_state.py` | No module-level mutable state; no process-wide current user; configuration is passed, not imported. | 2 | | `test_fitness_observability.py` | Every response and audit row carries a correlation id; nothing is swallowed; the outcome metric is measured. | 2, 4 | | `test_fitness_domain_invariants.py` | Money is a value object; status is a state machine; each rule has one home; refunds are idempotent. | 1, 3, 5 | | `test_fitness_generated_code.py` | Nothing admits to being unreviewed; no unbounded cache; no `Manager`/`Helper` class names. | 1 | | `test_fitness_contracts_and_async.py` | The published envelope is versioned; upstream is read tolerantly; delivery is idempotent; the migration reached version 2. | 2, 3 | | `test_fitness_deep_modules.py` | Narrow interfaces over real substance; boundaries are declared; no import-time side effects. | 5 | Three of them are green today. Two are green and worth having (`test_nothing_in_the_package_reaches_the_network`, `test_every_module_has_a_docstring`); one is green and nearly worthless (`test_no_domain_module_imports_sqlite3_directly`) and is kept as a worked example of a fitness function that measures the wrong thing — it passes only because the coupling it should catch is one import hop away. ## Writing your own The helpers in `_marks.py` are the whole toolkit: `source(module)` for text, `tree(module)` for an AST, `imported_siblings(module)` for the dependency graph, `modules()` for the inventory. Most useful fitness functions are ten lines of `ast` and one `assertEqual` against an empty collection — assert the *list of offenders* rather than a boolean, so the failure message names them.