First 10, 50 and 100 hours with CNA

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. Presets, targets and test names were checked against CMakePresets.json, cmake/UnitTests.cmake and the test sources at the snapshot; none of the commands was executed for this page.

The hour labels name stages of competence, not promises about how long CNA takes to learn. Read implementation beside tests and CMake. At each stage, produce a small diagram or trace yourself; if you cannot explain a relationship without the page open, repeat the route before editing that area. Every file, symbol, preset and test named below exists at snapshot 009d40f5.

ℹ

Before the first command. The presets and CTest filters on this page were checked against CMakePresets.json and cmake/UnitTests.cmake at this snapshot; they were not executed for this page. They need a CNA checkout of the next branch with its sibling repositories and submodules (Building: sibling repositories, submodules). The unit preset selects the STUB renderer but keeps the default SDL3 platform, so the SDL submodules are still needed, and window-creating tests need a display (Running the tests). There are configure and build presets but no test presets, so CTest is driven with --test-dir.

Stage 1: first 10 hours — find the running system

  1. Capture the exact tree and configuration. Read CMakeLists.txt, modules/CMakeLists.txt and CMakePresets.json in that order. Identify the physical module helper (cna_add_module), the CNA umbrella target, the difference between configure presets (17 visible plus the hidden base-ninja) and build presets (which only pick targets), and the platform, audio and renderer selectors (cmake/PlatformSelection.cmake, cmake/AudioPlatformSelection.cmake, cmake/RendererSelection.cmake). Exercise: explain why unit compiles STUB but does not validate Vulkan presentation. Then configure, build and list:
    cmake --preset unit                    # STUB, Debug, CNA_BUILD_TESTS=ON; examples, C API, net, video, Draco OFF
    cmake --build --preset unit            # builds the aggregate CnaTests into cmake-build-unit/
    ctest --test-dir cmake-build-unit -N   # this configuration's CTest inventory
    CnaTests cases are registered by gtest_discover_tests(... DISCOVERY_MODE PRE_TEST), so the list is produced from the built binary; build before listing.
  2. Trace construction before the loop. Read demo_2d Main.cpp for the caller, Game.hpp for member declaration order (platform_ first, then platformInstallation_, …, GraphicsDevice_, Content_, Window_) and Game.cpp for the constructor, InstallPlatform and DoInitialize. Exercise: draw which objects already exist when the derived Initialize begins, and explain why InstallPlatform cannot move into the constructor body (every member after platform_ may reach the ambient platform while it is constructed; the body runs after all of them). Check GamePlatformOwnershipTests.cpp — especially ExplicitPlatformIsOwnedAndInstalledBeforeGameMembers and AFailedConstructionLeavesNothingInstalled — and GameTest.RunExecutesLifecycleInDocumentedOrder in GameTests.cpp.
  3. Trace one frame and one failure. Follow Game::Run → RunLoop → Tick in Game.cpp, then the one-frame trace. Mark where platform events enter (Game::PollEvents), where fixed-step updates can repeat (the while over accumulatedElapsedTime_), and where draw and present can be suppressed (suppressDraw_, BeginDraw() returning false). Run the focused CTest filter in the configured build:
    ctest --test-dir cmake-build-unit -R 'GameTest\.RunExecutesLifecycleInDocumentedOrder' --output-on-failure
    ctest --test-dir cmake-build-unit -R 'GamePlatformOwnershipTest\.' --output-on-failure
    Exercise: explain why a passing lifecycle test cannot prove a real window appeared. (Read the test: it asserts call counts — one Initialize, one LoadContent, at least one Update and Draw — after probing for a hidden 64×64 window, and it calls GTEST_SKIP when the selected platform cannot create one, so on a host without a display GoogleTest reports it as skipped — current CMake versions pass that through to CTest as a skip, older ones may count the case as passed.)
  4. Separate the three selected backends. Read the build selectors, renderer descriptor and registry selection and the platform contract. Exercise: for SDL3 platform + VULKAN renderer + NULL audio, list which component owns events, GPU commands and audio silence. Then explain why TERMINAL + VULKAN is rejected at configure time (the _cna_terminal_renderers check in cmake/RendererSelection.cmake allows only SOFTWARE, PORTABLEGL, HEADLESS and STUB, and runs before any renderer dependency probe).

Stage outcome. The stage is complete when you can explain startup, one frame and ordinary exit on a whiteboard, with the selected platform and renderer named separately. Do not memorize every API.

Stage 2: first 50 hours — recover ownership and variants

  1. Draw the lifetime graph. Study the ownership master map, then Game and GraphicsDevice destruction and resource registration. In GraphicsDevice.cpp, inspect GraphicsDevice::Dispose(bool), destroyNativeResources and AddResourceReference; in GraphicsResource.cpp, the resource side. Exercise: predict what happens when a Texture2D survives Game destruction; locate the weak lifetime token that makes late cleanup safe (resourceDeviceLifetime_ in the device, graphicsDeviceLifetime_ in the resource). Explain why resource disposal precedes the renderer reset in Dispose(bool).
  2. Compare implementations of one contract. For the platform, compare SDL3 with Win32 or X11, and contrast Headless. For graphics, compare Vulkan command recording with EasyGL context-bound drawing or SDL_gpu queued passes. Exercise: trace the same resize or draw responsibility on two backends and write down what is contract-level and what is native. Do not infer parity merely because both compile.
  3. Trace non-render paths. Read events versus snapshots, the mixer and callbacks, the build-time asset pipeline and runtime load and cache. Exercise: draw a source texture → processed asset → ContentManager object path and mark which process owns each stage. Build the focused content tests after the unit configure and list what they contain:
    cmake --build --preset unit-content                 # builds CnaContentTests only
    ./cmake-build-unit/CnaContentTests --gtest_list_tests  # run from the repository root
    CnaContentTests is a focused iteration executable: it is not registered with CTest, so ctest will not run it and a ctest -R filter after building only this target finds nothing to execute. Inspect the real test names before choosing a filter, and run it from the repository root, which is the working directory CTest gives the CnaTests cases.
  4. Learn how tests are assembled. Read cmake/UnitTests.cmake beside the test architecture. Exercise: for one test source, locate its object group (cna_<group>_test_objects, where the group is the owning module directory, renderers, or integration for root tests), its focused executable (the CNA_TEST_FOCUSED_TARGET_<group> table: CnaRuntimeTests, CnaGraphicsTests, CnaPlatformModuleTests, …), the aggregate CnaTests registration, its working directory and the CI cell that runs it. Note that no workflow at this snapshot configures the unit preset; the general CI job configures OPENGLES3 with tests and examples on under Xvfb (what CI covers). Explain why a CTest skip (return code 77) or a GTEST_SKIP and a green result are not equivalent evidence.
ℹ

Changed recently in UnitTests.cmake. The discovered CnaTests cases now carry an output gate that joins the Vulkan validation pattern with an OpenGL4 GL-error pattern (cna_append_vulkan_validation_gate_pattern, cna_append_opengl4_gl_error_gate_pattern), so a case can fail on its log output even when its assertions pass; and the EasyGL test-suite gate lists only the five EasyGL identities, because OPENGL4 is its own family. A pure-CMake test, CnaSdlPrebuiltFingerprint, checks that the persistent vendored-SDL install is rebuilt when its inputs change.

Stage outcome. At this stage you should be able to identify the owner of an ordinary bug and tell whether the fix changes a common contract, a selected backend, or a serialized/ABI boundary.

Stage 3: first 100 hours — author and review changes

  1. Perform an independent investigation. Take one narrow issue and use the twelve source checks. Write the call path, ownership path, mutation points, test mapping and selected build axes before modifying source. Read the test's implementation, not just its name.
  2. Make one local patch, then one cross-cutting review. The worked changes rehearse a component lifetime regression, shared render-target behaviour, a Vulkan present/resize path, a platform event change and public API propagation. First add or adjust a failing test in the owning module; then make the smallest change. For graphics, run a second, independent backend or state explicitly why it could not be run. For public API, decide explicitly whether the C ABI needs a new route, and inspect binding ABI/version gates before claiming language support.
  3. Exercise failure modes. Reproduce constructor failure, explicit disposal, the normal destructor, a resize or second frame, or callback teardown as appropriate. Run the change→test matrix, then inspect ctest -N and the skipped cases for the actual configuration. Where native-host validation is unavailable, record the gap rather than silently upgrading compile evidence to runtime evidence.
  4. Review without an AI summary. From the diff alone, explain every changed owner, every possible invalidation of a borrowed pointer, each backend-capability assumption, each test assertion and every changed build configuration. If you cannot reproduce the author's trace from source, the patch is not yet reviewable.

Stage outcome. "100 hours" is not a credential. Readiness is demonstrated by an evidence-backed patch and an explanation of what remains unproven, not by time spent. Keep a local notebook of verified call paths. When a discovery is reusable, turn it into a correction to the Development pages at a new source revision with its links re-checked, following the maintenance & pin policy.

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