Choose one subsystem and own it

CNA snapshot 009d40f5  ·  Development › Human Takeover  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Module sizes come from the generated module index and from line and test-definition counts taken from the snapshot tree; commands were read from CMakePresets.json and cmake/UnitTests.cmake and were not executed. Oracle-backed math tests are described from their own headers, which record comparisons with recorded XNA 4.0 reference data; nothing was re-run.

CNA has 22 framework modules, 21 renderer families, 7 platform backends and 4 audio implementations, so a maintainer cannot own all of it at once. This page gives a procedure for choosing one subsystem and becoming the person who can answer for it, with a check that can fail at every step. It covers how to choose from CNA's own numbers, which subsystems are good first choices and why, the dossier to build, a seven-rung ownership ladder with a concrete proof at each rung, and the checks that move work from an AI assistant to the human who signs the change.

ℹ

Every number below was computed from the snapshot tree (the module index, plus line counts and a line-start scan for TEST, TEST_F and TEST_P definitions in each module's tests/ directory; over modules/ and tests/ the three macros give 12,610, the site's canonical figure). They describe size and test presence, not quality or pass results. Commands were read from CMakePresets.json and UnitTests.cmake; none was executed. Use Source ownership to look up who owns a behaviour; this page is about becoming that owner.

Selection criteria

Pick on evidence, not on interest. The criteria below are all answerable from pages and files that already exist; the third column says where.

CriterionQuestionWhere CNA records the answerPrefer for a first subsystem
Blast radiusHow far does a one-line change travel?the dependency matrix and the link edgesA leaf, or a value-like contract whose consumers one search can enumerate.
ReadinessCan you answer the construction, normal-path, failure and teardown questions?readiness questionsOne you can trace end to end in the first week.
Size and complexityHow many headers, sources and test files? How many lines?module index and a line count of your own (generated headers such as spirv_shaders.hpp inflate a family)A directory you can list on one screen; single-digit source files.
Test density and rolesHow many test definitions, and which roles: focused executable, link-closure probe, CTest name, CI cell?test target index, the group table in UnitTests.cmake, test rolesA focused executable and tests that assert behaviour. Density is not correctness.
Host requirementsDisplay, GPU, compositor, network, Windows or Wine, submodules, siblings?platform backends, CMakePresets.jsonNothing beyond the default configure's submodules and the sharp-runtime sibling.
ReachDoes the C ABI or a binding expose it?the C API coverage summary (its out-of-scope table) and the CnaCApi*.cpp filesKnow the answer before choosing; either is fine.
Risk classDestructive operations, threads and callbacks, sockets, generated files?ownership map, thread mapOne class, chosen deliberately.
Existing documentationIs there an internals page and a case study?internals mapYes: the page is the reference to check your own trace against.
  1. List two or three candidates and fill one row of the table below for each.
  2. Find each in the blast-radius matrix and copy its consumer set and minimum cross-check.
  3. Answer its readiness questions on paper. Any answer that needs the source is a reading assignment, not a reason to reject.
  4. Reject a candidate whose host requirement you cannot meet this month, or that carries more than one risk class.
  5. Commit to one for a fixed period. A second subsystem starts only after rung 5 on the first.

Suggested first subsystems, with reasons

Size is headers, sources and test files from the module index, then lines in include/ and src/ (comments included) and test definitions found by a scan of tests/. Renderer families keep tests as example programs, so their definition counts understate them.

CandidateSizeWhy it is a good first choice (read from source)Host and build needsWatch for
storage
Storage internals
3 / 3 / 1; 1,087 lines; 14 test definitionsA small module (three source files) with a real authority boundary: one process-static root, and a recursive delete (fs::remove_all in StorageDevice.cpp) that must stay below it. No platform edge (cna_core_headers plus Sharp Runtime only), a link-closure probe (probe_storage.cpp), the focused target CnaStorageTests, C routes in CnaCApiStorage.cpp with CApi_StorageSmoke, and the storage containment case study.None: a filesystem. The fixture removes <data root>/CnaTestsContent002StorageDevice on teardown, so point XDG_DATA_HOME at a disposable directory first.One test file is thin evidence for a destructive call. Path containment is shared with content and media, so its reach exceeds the module.
math
Math internals
24 / 17 / 27; 13,969 lines; 873 test definitionsThe highest test density among these candidates (873 definitions in about 14,000 lines), pure values, no host. MatrixOracleTests.cpp and BoundingSphereOracleTests.cpp compare against recorded XNA 4.0 reference data (matrix-oracle.txt, produced by run-matrix-oracle.sh), so the oracle habit can be practised on a module that needs no renderer. Focused target CnaMathTests; build preset unit-math.None to read or run the tests: the oracle data is committed. Regenerating it needs the XNA 4.0 reference assemblies, Wine and mcs, per the header of the script.Wide reach with silent failure: consumers include effects, transforms and the C ABI's value structures, and nothing throws when a convention moves.
design
Design internals
16 / 3 / 2; 1,191 lines; 25 test definitionsDepends on cna_math only, is outside the CNA umbrella (linked when named), has a probe and CnaDesignTests, and publishes no C routes. A minimal instance of the module template.None.Little runtime consequence, so it teaches structure more than behaviour.
phone
Phone internals
9 / 3 / 2; 1,061 lines; 18 test definitionsMaps Game lifecycle events onto the Windows Phone shell and runs a loopback HTTP listener thread: a small, bounded exercise in lifecycle and thread ownership. Outside the umbrella; only its tests link it; classified out of C scope by owner decision in the coverage scope table.Loopback sockets in tests; no display.No link-closure probe exists for it. Two risk classes at once (lifecycle and threads).
diagnostics
Diagnostics internals
2 / 1 / 1; 2,438 lines; 40 test definitionsNo CNA link edges, and a compile-time configuration: CNA_DIAGNOSTICS is OFF by default and the test file branches on CNA_DIAGNOSTICS_LEVEL. Good for learning that a green run depends on the configuration.None.Its interesting paths exist only at level 1 or 2, so a default configure exercises the smallest subset.
platform Headless
platform Terminal
Headless internals
Terminal internals
Headless: 2 files, 392 lines. Terminal: 22 files, 4,754 lines, 117 test definitions in 8 filesCompiled into every binary (Terminal on every non-Windows target), so the conformance suite compares them with the selected backend in one process (PlatformConformanceTests.cpp). CI configures both without a display: the cells “Headless + Headless + Null audio” and “Terminal + Software + Null audio” in platform-ci.yml. The platform module can also be built and tested alone through the standalone harness, which its own header says does not need the sharp-runtime sibling.Headless: none. Terminal: POSIX and a pseudo-terminal (the tests bring their own).Any change to the contract they implement reaches all seven backends. Terminal owns terminal-mode restoration and signal handling.
renderers/stub
renderers/headless
Stub internals
Headless renderer internals
Stub: 289 lines. Headless: 1,866 lines and 7 example programsThe two smallest implementations of IGraphicsRenderer: read StubRenderer.cpp to see how little a family must do, then HEADLESS to see validation and bookkeeping added. The unit preset selects STUB, and multi-renderer-ci.yml builds HEADLESS;SOFTWARE;STUB with CnaTests and no display or GPU.The default SDL3 platform and its submodules unless you select another platform and CNA_ENABLE_SDL=OFF.Their tests are example programs registered only when that renderer is selected (Stub_Smoke, Headless_*), so ctest -N in another configuration lists none. Read from source, not configured: the STUB example macro links SDL3::SDL3 unconditionally, where the HEADLESS macro guards it.

Poor first choices, with numbers

These are not unimportant; they cannot be traced in one pass. c-api has 178,863 lines in include/ and src/, 61 headers and its own test executables, and no CI job builds it. content has 60,962 lines and 1,871 test definitions. graphics has 63,571 lines and 2,906 test definitions. platform as a whole has 63,653 lines across seven backends. audio has 17,975 lines and callback threads. A native backend such as Win32 or Wayland needs its own host to prove anything (Win32, Wayland). Start with a candidate above, and use one of these as the second subsystem once the ladder has worked.

The subsystem dossier

The dossier is one working file per subsystem, kept outside the repository and dated to a snapshot. It is the artifact every later rung is checked against. Fill it in the order below; an item that reads “none” or “unknown” is a valid entry, and an empty one is not.

SUBSYSTEM DOSSIER: <module or family>     snapshot: <sha>     owner: <name>
 1 Contract     public headers; XNA or CNAEXT; C route file (CnaCApi*.cpp) or "none"
 2 Trace        public call -> runtime -> backend or host, as function names
 3 Selection    factory, registry or option that makes this implementation live
 4 CMake        target, link edges (PUBLIC/PRIVATE), umbrella member?, gating option, globs
 5 Lifetime     owner / borrower / registry / cache / ambient, drawn as a graph
 6 Generated    generated inputs and outputs, or "none"
 7 Tests        focused target, CTest names, probes, CI cell, skips (77, GTEST_SKIP)
 8 Siblings     another implementation of the same contract, and what differs
 9 Oracle       XNA reference data or "none": what it does and does not prove
10 Reach        C ABI routes, bindings, blast-radius row
11 Unknowns     what could not be established, and the test that would settle it

The table shows what each item asks and fills it in for storage, the candidate with a destructive call. Storage's own page is Storage internals.

ItemQuestionWhere to lookStorage, from source
1 ContractWhich headers are the promise, and who else promises it?Public headers under include/; the CNAEXT tag; the matching CNA/C/*.hStorageDevice.hpp (XNA shape) with the CNAEXT SetAppNameEXT; C header storage.h
2 TraceWhat runs between the call and the host?Implementation in src/; the module's internals pageSelector and open-container calls complete on the caller's thread; the root comes from EnsureStorageRoot
3 SelectionWhat makes this implementation the live one?source ownership: factory, registry or CMake selectionNothing selects it: storage has no platform edge, and an empty answer is a result
4 CMakeWhat does the module link, and who links it?The module's CMakeLists.txt; modules/CMakeLists.txt for the umbrella listcna_storage; PUBLIC cna_core_headers and Sharp Runtime Core.Base IO Runtime Threading; umbrella part; probe ModuleLinkClosure_probe_storage
5 LifetimeWho may still use the object when it dies?ownership vocabularyStorageContainer keeps a borrowed const StorageDevice*
6 GeneratedDoes any input or output come from a generator?generated versus handwrittenNone
7 TestsWhat fails, in which configuration, and what skips?test target indexOne file, StorageDeviceTests.cpp; part of CnaTests; focused CnaStorageTests; no dedicated workflow
8 SiblingsWhat implements the same contract elsewhere?Other implementations of the interface; other callers of the shared helperNo second implementation; the comparison partners are the other callers of PathContainment (content, content-pipeline, media)
9 OracleIs there recorded XNA behaviour?using the XNA oracleNone found under tests/reference/xna40 (searched by name)
10 ReachWhat else moves when this moves?storage boundaryCnaCApiStorage.cpp; CNA_GamerServices links storage

The ownership ladder

Each rung has a deliverable and a proof. The proof is a check that can fail, and a failed proof sends you back one rung; time spent is not evidence. Rungs 1 to 3 match the first two stages of First 10, 50 and 100 hours; rungs 4 to 6 use the method in How to understand code you did not write and the rehearsals in Worked human changes.

RungYou doYou hand inProof of understanding, and when it fails
1 ReadRead the module's CMakeLists.txt, public headers, implementation and tests in that order, then its internals page.Dossier items 1, 3, 4 and 7, each a file list with a one-line role.Closed-book listing: name every source file of the module and its role, then compare with the module index or git ls-files. Fails on any file or directory you cannot place.
2 TraceFollow one public call to the host, and one object from construction to destruction, using function names.Dossier items 2 and 5: the call trace and the ownership graph.Redraw the ownership graph from memory and diff it against the source declaration order and the ownership map. Fails on a missing borrower, registry or cache.
3 ReproduceConfigure a build, list the subsystem's tests, run one focused case, and explain each skip.A reproduction record: the selected axes, the exact commands, ctest -N output, skipped cases and why.Write the expected test count and the expected skips first, then compare with ctest -N and --gtest_list_tests. Fails when a skip is unexplained or a test you named is not in this configuration.
4 TestWrite one characterization test for behaviour you could only read, before any change. Then make deliberate one-line breaks on a scratch branch.The test, plus a table of breaks: predicted failing tests, actual failing tests.Predict before running. Fails when a break you expected to be caught fails nothing (a test gap) or fails something you did not predict (a missed edge).
5 PatchMake the smallest change, with the invariant it must preserve written before the edit.The diff and an investigation record naming the axes, owner, order, other implementations and validation run.Predict the outcome of a lifecycle or ordering change (which destructor runs when, which callback still fires) and confirm it with a focused test or a sanitizer run. Fails when the prediction and the result differ and you cannot say why.
6 ReviewReview a diff in your subsystem written by someone else, human or AI.A review memo in the style of worked changes: owners, borrowed pointers, backend assumptions, untouched variants.Explain the diff without a summary: only the diff and the source are open, and a second reader can tie every claim to a file and function. Fails on any claim that cannot be so tied.
7 TeachHand the subsystem to someone else, or correct the Development page where your reading disagrees with it.A source-pinned correction, or a walkthrough with the dossier as the script.The learner passes rungs 1 and 2 against your dossier, and you answer their “what breaks if…” questions from source without notes. Fails when you have to look something up that the dossier should hold.
⚠

Rungs 3 and 4 can be destructive for storage. The unit-test fixture recursively removes CnaTestsContent002StorageDevice under the per-user data root, and a deliberately weakened containment check could follow a symlink out of it. Point XDG_DATA_HOME (or LOCALAPPDATA, or HOME, whichever the host uses) at a disposable directory holding an outside sentinel file before running any storage test, and never run a weakened build against real data (storage is a destructive authority boundary). Math, design and diagnostics have no such hazard.

Rung 3 in commands

The presets are the ones on the curriculum page; the focused target names come from the group table in UnitTests.cmake. Focused executables are built on request and are not registered with CTest, so run them directly from the repository root, which is the working directory CTest gives the discovered cases.

cmake --preset unit                                   # STUB renderer, tests on, examples off
cmake --build cmake-build-unit --target CnaStorageTests
XDG_DATA_HOME=/path/to/disposable ./cmake-build-unit/CnaStorageTests --gtest_list_tests
ctest --test-dir cmake-build-unit -N                  # the whole configuration's inventory
cmake --build --preset unit-math                      # math instead: builds CnaMathTests

Checks that replace AI dependence

An assistant is useful for finding files and for drafting; it is not a source of ownership, because ownership is a claim about consequences that only a person can be held to. The aim is not to stop using one. It is to make every use pass through a step that a person performs and can be checked on. Run these at each rung, and keep the results in the dossier.

CheckHowPassA failure means
Blind lookup drillChoose ten behaviours from the lookup table. Without the page, name owner, header, implementation and test file, then diff.9 of 10 for your subsystem; 6 of 10 elsewhere.Your map of the repository is borrowed, not held.
Redraw and diffDraw the ownership graph on paper, then compare it with the source and the ownership map.No missing edge; every borrowed pointer labelled.You are trusting names such as “manager”.
Predict, then runBefore a lifecycle, ordering or option change, write which tests fail and which callbacks fire. Then run the focused target.Prediction equals result, or the difference is explained.The mental model is wrong somewhere.
Mutation tableBreak one line at a time on a scratch branch and record predicted and actual failures.Every break is caught, or the gap is written down as a missing test.The tests are trusted more than they earn.
Diff without a summaryRead a diff with no description. Explain owners, invalidations, capability assumptions, assertions and configuration changes.A second reader can verify each sentence against source.Review is delegated to whoever wrote the summary.
Claim ledgerFor each statement from an AI answer that you rely on, write the file and function you opened yourself. Unopened claims stay marked unverified.No unverified claim supports a decision.The answer is being used as evidence.
Opposite argumentAsk for the strongest case that your fix is wrong, then find the source line that settles it.You can point at the deciding line.You cannot yet tell a good argument from a fluent one.
Tools-only dayDo a rung's work with only rg, the module index, CMake and the tests.The rung's deliverable is unchanged.The search step, not the reasoning, was the dependency.

The pass thresholds are proposals to adjust for the size of the subsystem, not CNA policy. Which uses stay open changes with the rung. At rungs 1 and 2 an assistant may list candidate files, but every conclusion must come from files you opened. At rungs 3 and 4 it may draft tests, and you write the prediction before it runs. At rung 5 it may propose a patch, but you write the invariant and the predicted effects first. At rungs 6 and 7 no summary substitutes for reading: the review memo and the correction to the pages are yours.

Worked example: the first four rungs on math

Math has no host requirement and no destructive call, so the rungs can be rehearsed without care for side effects. Rung 1. Read math/CMakeLists.txt, then Matrix.hpp, Matrix.cpp and MatrixTests.cpp; the closed-book listing is the 17 source files and what each type owns. Rung 2. Trace Matrix::Multiply and one consumer outside math. The matrix row says a change here can alter rendering silently, so also record where the value structures cross into the C ABI (CnaCApiMatrix.cpp). Rung 3. Build CnaMathTests, write down the expected number of discovered tests before --gtest_list_tests, then read the header of MatrixOracleTests.cpp. It records that XNA accumulates dot products wider than float and narrows once, and that a float accumulation disagreed with XNA in entries a rotation leaves near zero; explain in your own words why the disagreement shows up only there. Rung 4. On a scratch branch negate the sine term in Matrix::CreateRotationZ, predict whether the failures stay inside CnaMathTests or also reach CnaGraphicsTests, run both, and record the difference as the first row of the mutation table. A characterization test needs no CMake edit: the test glob in UnitTests.cmake picks up a new file under modules/math/tests/ at the next build, because the glob is CONFIGURE_DEPENDS.

Scope, exit and handover

  • Scope guard. Owning a subsystem includes its tests, its CMake, its C route file if it has one, and the Development page that describes it. It does not include a backend you have not read, and a patch that leaves the module changes the blast radius; return to the matrix first.
  • Uncertainty is part of the deliverable. Record what the tests do not prove, using the register in Known uncertainty and history. Renderer-specific and host-specific behaviour stays unproven until run on that host.
  • Review of AI-written areas. The method for large generated changes is on Review large AI-generated areas without trusting them; oracle-backed claims are covered on Using the XNA oracle as evidence.
  • Handover. The dossier, the mutation table and the last review memo are what a successor reads first. If they cannot rebuild your understanding, rung 7 is not done.
  • Re-pin. Before trusting a dossier for a new patch, compare your branch with the snapshot and re-audit every changed path it depends on (how these pages are pinned).

The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.