Blast radius and readiness
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. The matrix rows are review hypotheses read from source and CMake at 009d40f5; no build, test or CI run was executed for this page.
Locate the physical file, but assess risk from the contract it changes. A private helper may have one caller; a public enum, virtual method or ownership rule can reach every renderer, platform, serialized asset and binding. The table below gives review hypotheses to prove or refute by call search, not an automatic permission to stop testing, and the sections after it turn “how far can this go?” into readiness and self-review questions to answer before an edit is written.
Typical dependency and evidence radius
| Change | Likely consumers and failure mode | Minimum cross-check |
|---|---|---|
| Math value or coordinate convention | Graphics effects, vertex transforms, collision and input geometry, serialized values and the C ABI's math value structures (their layout is recorded in an ABI baseline). Silent image changes are possible because nothing throws. | Math tests (CnaMathTests, unit-math) plus one rendering oracle; if a public layout changes, the header baseline check and the bindings' layout checks. |
| Game frame or component logic | Update/Draw order, the fixed-step clock, FrameworkDispatcher::Update, platform events and manager callbacks. The C ABI's CGame overrides the lifecycle virtuals, so a change to when Initialize, LoadContent, Update, Draw, BeginDraw or EndDraw runs reaches every binding's callbacks. Emscripten has its own loop path, and the diagnostics frame scope wraps Tick. | Game lifecycle and component tests (GameTest.*, GamePlatformOwnershipTest.*), focused runtime tests, at least one real loop configuration; the thread and callback map for anything touching component lists. |
GraphicsDevice state or validation | Every renderer through the neutral contract, effects, resource lifetime, capability reporting (the GraphicsCapability members and the renderer capability profile), the thread-context lease the GL renderers use, and the C API graphics routes. | Neutral graphics tests (CnaGraphicsTests), independent CPU, GL and explicit-GPU implementations, resource and disposal review, binding review. |
GraphicsResource registration or disposal | All buffers, textures and targets, backend-native release, late C++ destructors and the weak device-lifetime token; in the C ABI, owned-child counters gate cna_game_destroy. | Explicit Dispose, device-first and resource-first destruction, ASan, and backend live-resource tests (the HEADLESS renderer can list live resources). |
IPlatform or the event model | Seven platform implementations, Game::PollEvents and the input bridge, window, presenter and native surface, capability conformance, and the audio and sensor paths that reach the platform. | HEADLESS contract, the *PlatformConformance* suites parameterised over every available implementation, native backend mapping fixtures and the affected live host. |
IGraphicsRenderer or a renderer descriptor | 25 renderer identities over 21 implementation families; selection, the generated registry, the descriptor gate and every native implementation. | Compile all relevant families; the descriptor gate (see below); the neutral contract; several unlike runtime backends. |
| One native renderer implementation | Its own resources, command timing, platform surface and cross-backend semantics. | Focused family oracle, second-frame, resize and disposal checks, the neutral suite, and an independent renderer comparison. Vulkan and OpenGL4 configurations also carry an output gate that fails any CTest whose output contains the renderer's validation or GL-error marker. |
| Content format, reader or writer | Build-time importer, processor and writer (cna-content), runtime reader and cache, fixture assets, version handling and the C ABI content routes. | Round trip plus malformed and old-format cases, affected runtime load tests, and the XNB conformance entries labelled content/xnb. |
| C ABI header, handle or error rule | Every external binding (separate projects, recorded against ABI 0.21.x while the snapshot exports 0.29.0), desktop exports, wasm exports, thread and finalizer ownership. | Pure-C smoke tests, header audit, export and ABI-baseline checks, the wasm link contract, and each maintained binding's own pinned integration tests. No CI workflow builds the C library at this snapshot. |
| CMake selector or public define | Configure refusal (reserved and unknown names), the generated registry, the link closure, ABI-visible default macros and conditionally compiled tests. | Valid and invalid configure pairs, single- and multi-identity links, runtime selection, and the CI matrix. |
| Test helpers, CTest properties or harness targets | Every registration made through cna_register_renderer_test (working directory, output gates, display policy), the directory-wide skip code 77, the source filters that decide which tests exist in a configuration, and the standalone harness executables. A wrong edit silently changes what “green” means for many suites. | Compare ctest -N before and after in at least two configurations; check that gates and skip conventions still attach; run one suite that must fail to prove the gate can. |
| A lock, callback or teardown-order rule | Every thread that can run while the changed object mutates: audio devices, sensor sessions, loading threads, foreign callbacks. | The five answers on the thread and callback map, plus the matching sanitizer run. |
The concrete target and CI route for each row is in what to test after changing X. A changed public header may trigger broad recompilation even if the runtime behaviour is local. A change under one renderer may still violate a shared coordinate convention; locality is a conclusion after comparing contract and tests.
Gates that widen a change's reach by construction
- Descriptor gate. A renderer descriptor is the one file per family that a single-renderer build never compiles.
RendererDescriptorGate.cmakeadds an object library (cna_renderer_descriptor_gate, optionCNA_BUILD_RENDERER_DESCRIPTOR_GATE, default ON) that compiles every registered family's descriptor that the configuration does not already compile, and it fails configuration when a registered identity has no descriptor declaring the namespace the generated registry will call. The DirectX 9, 11 and 12 families are excluded because their descriptors need the Windows SDK, and Vulkan is excluded where no Vulkan headers exist. A descriptor edit therefore can break a build that never selected that family. - Output gates.
TestHelpers.cmaketurns the[Vulkan Validation]and[OpenGL4 GL Error]lines into failures (FAIL_REGULAR_EXPRESSION) for every registration in a configuration where that renderer can exist, andUnitTests.cmakeapplies the same patterns to the individually discoveredCnaTestscases. The exemption lists are empty by design. A renderer message that only appears during teardown fails the test that caused it, so a lifetime change can fail an unrelated-looking CTest. - Skip convention. Tests registered in
UnitTests.cmake's scope getSKIP_RETURN_CODE 77, and module-local registrations apply the same convention through a helper, so a test that self-detects a missing display or GPU is reported as skipped, never as passed.
Readiness questions before editing a subsystem
Answer the question for the subsystem you will touch. If any construction, normal path, failure or teardown edge is still unknown, write a characterization test or mark the risk in review before implementing a new feature.
- Runtime. Can you trace
Gameconstruction beforeRun, fixed and variableTick, component snapshots,Dispose(true)versus the destructor, and ambient platform restoration? If not, read startup, one frame, shutdown and their tests. - GraphicsDevice. Can you name who constructs it, what the descriptor knows before construction, how state reaches a renderer, which registry entries are borrowed, when
Presentoccurs, and why renderer teardown follows resource disposal? Read GraphicsDevice internals, renderer selection, textures and render targets and one draw trace first. - One renderer. Can you identify its public identity and physical target, host gate, native device and surface owner, draw-time versus submit-time state, resource retirement, resize behaviour and test oracle? Use Vulkan, EasyGL, OpenGL4 or SDL_gpu as a worked model, and the Software, HEADLESS and STUB pages for the CPU and no-window identities; then audit the chosen family itself with the graphics backends map. A family without a deep page in the Development area is a research assignment, not evidence that it is simple.
- One platform. Can you explain factory selection, window ownership and adoption, event translation, input snapshots, logical and drawable size, the surface service, shutdown and native-host tests? Compare SDL3 with the selected backend (platform backends), not only its own header.
- Content, audio or interop. Can you trace a source asset to its runtime object, cache and disposal; a playback object to its device callback and callback barrier; or a language wrapper to a typed C handle, the creation-thread check and child-before-parent destroy? The content, audio and C ABI pages are prerequisites, and the ownership and thread maps carry the cross-subsystem rules.
Self-review questions that catch AI-era patch hazards
- Did the patch change a contract, or only one implementation? Search every override and registration rather than trusting IDE references in the active build: a build compiles one platform, one audio implementation and one or a few renderers, and a reference in an excluded source is invisible to it.
- Does a new state field need copying into deferred draw records, reset on resize, serialization, ABI exposure or cleanup on failed construction?
- Could a borrowed pointer outlive its owner on explicit
Dispose, destructor, failed constructor, fallback or foreign-handle release? - Is a test actually linked and run in this CMake configuration, or excluded or skipped?
UnitTests.cmakedrops whole source groups byCNA_PLATFORM(the SDL3, SDL2, X11 and Wayland tests), by renderer and by optional module, so a green run may simply not contain the test. Did the test assert output rather than merely construct the class? - Does the patch preserve old public ordinals, structure size and version, C error codes and binding ownership rules?
- Did you test the first frame, a later frame, resize or reset, disposal and retry where the subsystem supports them?
A good review memo explains why unaffected variants really are unaffected. The investigation record is the shortest way to make that claim auditable, and Worked human changes shows the memo applied to five rehearsed changes.
Storage is a destructive authority boundary
A change to StorageDevice, StorageContainer or the shared PathContainment helpers (PathContainment.hpp) reaches both saved-game paths and recursive delete (DeleteContainer ends in std::filesystem::remove_all). Its scope is larger than the small storage module (Storage internals): the app-name extension also sets Sharp Runtime isolated storage's root, and the helper is used by the content readers, manifest sidecars, playlist parsing and the build-time content routes. Before editing, establish exactly which process-static root is active (StorageDevice.cpp: EnsureStorageRoot, SetAppNameEXT), which returned container borrows which device, how EndOpenContainer validates a title, and how each file operation rejects an escape. Validate absolute, parent, dot and symlink cases against an isolated directory with an outside sentinel.
The existing fixture recursively removes a per-user directory during teardown (StorageDeviceTests.cpp): its root is derived from the same environment chain as production, so inspect the resolved target and point XDG_DATA_HOME (or LOCALAPPDATA, or HOME where those are what the host uses) at a disposable directory before running it. Changing a path helper requires reviewing content and every other caller too, not only storage's tests. The full worked review, including what the tests do not prove, is Case study: storage containment; the user-level rules are in Storage: path containment.
Related pages
The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.
- User guide
- Renderers: combination rules · Platform support: the separate axes · Storage: path containment
- Architecture
- Architecture · Architecture overview
- Internals
- GraphicsDevice internals · Graphics backends · C API internals
- Maintainer workflow
- How to understand code you did not write · Thread and callback map · Ownership and lifetime master map
- Tests and validation
- What to test after changing X · Verification: CI scope
- Reference
- CMake option index · Test target index