Review large AI-generated areas without trusting them
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. Counts come from the TARGET tree and Git objects; guards were read from CMake, scripts and workflows, not run. Test outcomes quoted from merge messages and plans are CNA's own records, not reproduced. Retired renderer identities are deliberately not named.
CNA was built at scale with AI assistance, and the tree carries the paper trail: commit subjects with task IDs, 106 plan files, frozen audit and remediation archives, root-level handoff notes, and comments that cite tasks. This page says how far each of those can be trusted, which of CNA's own mechanical guards are stronger evidence than any prose (and what each one really enforces), and how to review a very large diff without trusting its author, using the snapshot's own head merge as the worked example. Read it before you accept a change because a plan row, a status banner or a comment says it is done.
The rule
Current source and active CMake are authoritative. Plans, audits, handoff notes and comments explain intent and history, and can be stale. CNA's own files say the same about themselves. The archives audit/AUDIT_FINAL_REPORT.md, remediation/README.md and modularization/RECONCILIATION.md each open with a banner that they predate the 2026-09-17 renderer curation and must be read “as a fact about that date, never as current CNA support”. plans/README.md says the status recorded inside each plan is authoritative and refuses to copy task counts into the index. 26 of the 106 plan files open with a retired-renderer, superseded or rejected-candidate banner (24 use the words “retired renderer — historical record”). AGENTS.md and CLAUDE.md tell an assistant not to treat “old CNA code or AI-generated stubs” as authoritative when they conflict with the FNA reference API.
In practice: behaviour is answered by the implementation, the active CMake that compiles it and the tests registered for a given configuration; why, by git log and the plan; whether something is supported, by the registry (cmake/RendererIdentities.cmake), the CI workflows and Known uncertainty and history.
How the assistance shows in the tree
The instruction files address assistants directly (porting conventions, CNAEXT tagging, “always commit after finishing a task”). Some archives are written as resume prompts: audit/AUDIT_PROGRESS.md says that if context is lost the session should resume from that file and continue “fully autonomously” with “no further questions”, and the last section of NEXT.md is a resume prompt for a session on the DIRECTX9 branch. Those are instructions to a past agent, not project policy and not authorization for you. Git cannot separate assistant-written from human-written work: all 8,899 reachable commits carry one author name, none of the 2,877 commits since the v0.1.0-alpha.1 tag has a Co-authored-by trailer, and 2,765 of those 2,877 carry a PGP signature header, which attests the committing key and not that anyone read the diff.
The artifacts and how far each can be trusted
Figures were counted from the TARGET tree and history at 009d40f5; patterns are simple, so treat the percentages as approximate.
| Artifact | What it is at the snapshot | Use it for | Do not use it for |
|---|---|---|---|
| Commit subjects and task IDs | 2,877 commits since the tag; 2,854 subjects follow type(scope): (for example feat(Task GL4-0025): compute programs, storage and constant buffers on OpenGL4); 395 carry (Task …) or (Tasks …); about 2,500 (87 %) contain a PREFIX-number token. AGENTS.md asks for one task per commit, yet 28 subjects use a range and the widest, MOD-713..MOD-731, names 19 IDs. | Finding the plan that holds the evidence: GL4-0nnn is plans/plan_opengl4_modern_graphics.md, SMG is plans/plan_sdlgpu_modern_graphics.md, STREETS is plans/plan_street_sdlgpu.md, WGF is plans/plan_webgpu_failing_eight.md. | Scope. An ID can have no commit (GL4-0001 and GL4-0002 are plan-only rows), and one commit can carry 19. |
| First-parent merges | 713 first-parent commits since the tag: 677 direct commits and 36 merges (47 merges in all, 16 named merge(<Campaign>)). The other 2,164 commits sit inside merged branches. | The integration story: what landed when, and the summary numbers in merge messages. | The content of a “fold” merge, which can carry edits no branch commit shows (worked example below). |
| Plans | 106 Markdown plans in plans/, each a table of task rows with status and evidence; one more, plan/plan_compilation.md, sits in the singular plan/ directory and is not in the index. | Intent, the decision trail, what was measured on which host, and where CNA recorded its own corrections. | Current behaviour or “done”. See the examples below the table. |
| Audit and remediation | audit/ holds 2,493 files, 2,370 of them per-file *.audit.md reports (the final report counts 2,297 eligible ones); remediation/ turns them into 105 root-cause tasks and is marked a closed historical archive. | Leads: a finding may still be true. | Current defects. remediation/README.md says tasks marked “Verification required: YES” rest on static analysis that was never executed, that CTest results for about 220 tests were meaningless until REMED-BUILD-001, and that some tests “assert the bug”. |
| Spikes and modularization records | spikes/: 26 probe directories. CLAUDE.md keeps a probe while its renderer is a candidate or supported and removes it on retirement. modularization/: 142 files of inventories from the 2026-08 restructuring. | A recorded measurement (the six xna-*-spike probes) or the layout at a date. | Current layout. Win32DirectXIntegrationTests.cpp cites a spike directory named win32-directx-spike, which does not exist (only win32-spike does). |
| Root notes | NEXT.md: 6,743 lines, 66 second-level sections, newest dated 2026-09-20; 11 topical NEXT*.md files and two handoff_*.md files, three of the 14 marked as retired-renderer records. known_bugs.md (75 lines) dropped retired-renderer entries; misc/known_bugs.md has 382; AUDIT.md is an XNA API audit of 1,966 lines with dated notes; CHECKLIST.md is the per-file porting checklist. | Dated context and the reasoning behind a change. | Live status. NEXT.md and AUDIT.md were last changed by dc808e734 (2026-09-20), followed by 194 commits up to TARGET, although AGENTS.md says to update them at task end; NEXT.md contains none of the GL4, SMG, STREETS, WGF, VMG or GTI IDs. |
Documents in docs/ | 167 Markdown files in docs/. Some are dated measurement reports (docs/d3d9-divergence-report.md: “Status: 2026-07-15”, 31 scenes); some are living design pages (docs/opengl4-renderer.md, docs/physical-modules.md); docs/removed-renderers.md is a tombstone with the reserved ABI values. | Rationale and dated evidence, after reading the status line. | A count or table you have not compared with the registry or CMake. |
| Instruction files | AGENTS.md has 493 lines and CLAUDE.md 678; they have diverged (see the drift table). | The conventions the code was written under. | A description of the code, or of the current FNA/XNA tie-break. |
| Comments citing tasks | 2,318 of 5,427 source, build, script and data files under modules/, cmake/, scripts/, tools/, tests/ and examples/ cite a plans/plan_*.md path: 6,733 citations of 83 plans. | Which campaign to read and why a line exists. | The comment's claim about behaviour. Ten citations name four plan files that do not exist at TARGET (plan_devices_phase4/6/7/8.md; commit 4cb7578b6, “Old plans were deleted”, removed them from the repository root on 2026-07-07, and the comments cite them under a plans/ path they never had), including doc comments in the public headers Accelerometer.hpp, Gyroscope.hpp and SensorBase.hpp. |
Plan rows are claims, and CNA's plans record their own corrections. SOFTWARE-375, 376 and 390 were closed as done, and SOFTWARE-457 (2026-09-12) records that Microsoft's assembler invalidated the evidence behind them: the fixtures used operand forms the compiler forbids, and both CNA compile paths had accepted seven forged violations (plans/plan_software.md). plans/plan_webgpu.md counts 200 done rows and one open one (counted from its own row tables by tools/count_webgpu_plan_status.sh) and says “read the row count as a count of rows, not as a parity claim”; the plan plans/plan_webgpu_failing_eight.md then records that ctest -L WebGPU had eight failures on rows marked done, three of them tests asserting behaviour the framework had deliberately changed: “a plan row being green says the work was done, not that the test still describes the framework it was written against.”
Drift found while verifying these pages
Each row was checked by reading the named source at 009d40f5. It is a sample, not a census; the point is the shape of the mismatch.
| Where the claim lives | What TARGET does |
|---|---|
The IsLatched() comment in GraphicsRendererSelection.hpp: the selection latches when the first GraphicsDevice begins construction. | GraphicsDevice::resolveRenderer in GraphicsDevice.cpp calls GraphicsRendererSelectionAccessEXT::Latch only after a candidate renderer was created, and its own comment says latching up front “was the first cut, and it was wrong”. Two comments disagree; behaviour follows the implementation (latch internals). |
Device-loss comments: GraphicsDevice.hpp (deviceStatus_) says only the Direct3D 9 renderer ever calls the device-event callback; GraphicsDevice.cpp (createRenderer) says nine of ten renderers never do. | Calls to RendererDeviceEvent::Lost exist in six families: Vulkan, WebGPU, Direct2D, DirectX9, DirectX11 and DirectX12. The comments describe a ten-renderer world. |
The “Xlib threading” comment in X11Platform.hpp: every Xlib call goes through one per-instance mutex. | The only mutex in modules/platform/src/X11/ is the process-wide policy mutex in X11Error.cpp; XInitThreads is deliberately not called. |
Comments in Sdl3Platform.cpp and X11Platform.cpp cite Game::UpdateInput(). | No such function exists under modules/; the pump is the tail of Game::PollEvents. |
docs/platform-abstraction.md: the “Implementations” table lists five backends. CLAUDE.md lists WIN32 as a reserved identifier that fails configuration. | The table omits X11 and WAYLAND, which the same document describes elsewhere. cmake/PlatformSelection.cmake defines seven implementations, offers each only where it can build, and refuses WIN32 as reserved only off Windows targets (a MinGW cross-build from Linux targets Windows). |
Stale counts in prose: cmake/Tests/ModuleProbes.cmake still describes an earlier registry (it “pins the 42 public renderer identities”); scripts/check_renderer_descriptors.py says a build “compiles all 45”; docs/c-api/LIMITATIONS.md says “Four of the 49 renderers are built and run here”; the header of general-tests-ci.yml says “all 14 renderers”. | The registry holds 25 identities over 21 families. The enforced number lives in the table inside scripts/check_renderer_identities.py. CNA's own row RTR-P13-8 (plans/plan_runtimerenderer.md) says a count in prose is “a fact with no owner”. |
cmake/PlatformHotPathLint.cmake contrasts itself with a ratchet that “warns because the whole tree is over budget by design”. | cmake/PlatformRatchet.cmake defaults CNA_PLATFORM_RATCHET_STRICT to ON, and tools/platform/sdl_budget.json records a budget of 0 files and 0 references with an irreversible floor of 0. |
TODO.md, a sensors roadmap last changed by a commit dated 2026-04-11, lists Gyroscope, Compass and Motion as unchecked. | modules/devices/src/Sensors/ holds implementations of all three and Android sensor backends. |
docs/cross-renderer-parity-fixtures.md: EasyGL, WebGPU and SDL GPU register the fixtures; the “Current fixtures” table lists 13. | ParityFixtures.cmake lists 32 fixtures, and four renderers call cna_register_parity_fixtures() (OpenGL4 joined in the snapshot's head merge). |
scripts/run-oracle-corpus-diff-easygl.sh defaults DISPLAY to :0, calling that “this project's own CNA_TEST_DISPLAY default”. | Since GTI-0001 that variable defaults to empty (cmake/TestDisplayPolicy.cmake), and CLAUDE.md forbids running oracles on the owner's live desktop; the script still falls back to :0 when DISPLAY is unset (by reading; not executed). |
AGENTS.md calls FNA “the authoritative behavioral and API reference”. | CLAUDE.md calls it the “working” reference and adds a tie-break decided on 2026-09-04 (0e62e7368): where measured Microsoft XNA and FNA disagree, CNA follows XNA. AGENTS.md has no such section, defers to CLAUDE.md for layout and lacks the Platform Boundary chapter; CHECKLIST.md says FNA is authoritative for behaviour and the XNA reference assemblies for API surface. |
CNA's mechanical guards: what each really enforces
A guard is stronger evidence than prose only for what it matches, in the configuration that runs it, and with the exit code it actually produces. The table gives, for each, when it runs, what it enforces and what it cannot see. Registrations were read from CMake and workflows; nothing was executed.
| Guard | Runs | Enforces | Blind spot |
|---|---|---|---|
scripts/check_renderer_identities.py | CTest RendererIdentityRegistry; a step in multi-renderer-ci.yml | The 25 identities agree across the enum, the CMake list and the runtime registry chain; C ABI values are pinned and retired ones reserved; retired names are not selectable; counts stated in docs/runtime-renderer-selection.md, docs/renderer-expansion-candidates.md, docs/physical-modules.md and plans/plan_platform.md match. | Counts only in those listed files and only in the phrasings “N public identities” and “N implementation families”; LIMITATIONS.md and comments are outside it. |
cmake/RendererIdentities.cmake and the CnaRendererRetired_* script-mode tests | Configure time; CTest | An unknown renderer name given to CNA_GRAPHICS_RENDERER or as a member of CNA_GRAPHICS_RENDERERS is refused by name, and a retired name on every route (also CNA_RENDERER_<X>); an unknown CNA_RENDERER_<X> switch is not checked. One test per retired identity is generated from the list, plus eight route and control cases. | Says nothing about a public renderer's quality. |
cmake/RendererDescriptorGate.cmake, with scripts/check_renderer_descriptors.py and scripts/check_runtime_renderer_discipline.py | Configure time (CNA_BUILD_RENDERER_DESCRIPTOR_GATE defaults ON); CTest CNAEXT_RendererDescriptorsParse, RuntimeRendererDiscipline | Every family's descriptor translation unit is compiled in every configuration (an object library nothing links); a registered identity without a matching descriptor namespace fails configure; no CNA_RENDERER_* macro may appear in modules/graphics/src/. | Compiles descriptors, does not check what they answer. DIRECTX9, DIRECTX11, DIRECTX12 (and Vulkan without SDK headers) are compiled only by their own family builds. |
cmake/PlatformRatchet.cmake | Configure time; on by default and strict by default | SDL references in production files outside the allowlist must stay at zero (tools/platform/sdl_ratchet.py, budget file at 0/0); a separate audit classifies test and example uses. | A textual match, comments included. Skipped with a status line when Python 3 is absent; passing results are cached by input fingerprint. |
cmake/PlatformHotPathLint.cmake | Configure time, hard error | No platform call through a platform receiver inside a loop whose header mentions per-pixel, per-vertex, per-fragment, per-sample or per-event tokens; an annotation needs a reason. | Its own docstring: both judgements under-report, so a pass is not proof. |
cmake/TestDisplayPolicy.cmake | Configure time; CTests CnaTestDisplayPolicy, CnaTestDisplayPolicyAsSubproject, CnaTestDisplayIsolation | Registered tests do not target the live desktop: CNA_TEST_DISPLAY is empty by default, :0 needs CNA_TEST_ALLOW_LIVE_DISPLAY=ON, every test carries a Wayland guard (non-Windows); the last test inspects ctest --show-only=json-v1 of the configured tree. | Registrations, not what a script does inside a test (see the :0 fallback above). The last two need CMake 3.28 or newer. |
cmake/Tests/ModuleProbes.cmake with scripts/check_module_link_closure.py | CTest, native builds with tests on | Up to 15 minimal probes, one per module alias; the probe's link line must not contain forbidden archives (and must contain required ones). Native-SDK-free and Vulkan-only closure gates exist for single-renderer HEADLESS and VULKAN builds. | Reads CMakeFiles/<target>.dir/link.txt and exits 77 (skip) without it; the unit, tests, multi-renderer and cnaext presets and the general-tests and multi-renderer workflows all use Ninja, and no workflow names these tests. |
scripts/check_no_posix_setenv.py | CTest CNAEXT_NoPosixSetenv whenever tests are built and Python is present | No setenv or unsetenv outside comment lines in modules/, tools/, tests/ (they do not exist on MinGW-w64). | By reading, the native Wayland platform code and tests contain such calls at TARGET; CNA's plans list this test among standing failures, and the single-entry allowlist in general-tests-ci.yml does not name it. Whether that job is green was not verified. |
scripts/validate_direct2d_plan.py, tools/count_webgpu_plan_status.sh, the CApiLimitations and CApiDocExportCounts tests | Direct2D validator: a manual-dispatch workflow only; the C API tests: CTest | Plan statuses must match evidence and cited paths and tests must exist (Direct2D); status counts derive from rows; C API document counts derive from the inventory. | Rows, not behaviour: a green row can stay green after its test breaks. |
tools/audit_xna_runtime_surface.py | Manual; not registered by CMake or a workflow (searched) | A census of documented XNA types and members against CNA's public headers. | Representation only; its default input is a directory beside the repository. |
Sibling audits in the same style are scripts/check_renderer_target_discipline.py (RendererTargetDiscipline), scripts/check_renderer_combinations.py (RendererCombinationRegistry), scripts/check_removed_renderer_api.py (RendererCurationApiDecisions) and the CNAEXT guard, matrix, naming, nodiscard and Doxygen checks (scripts/check_cnaext_guards.sh and its neighbours).
Three reading rules follow. Know when a guard runs: configure time, CTest, CI or by hand. Know what it matches: a regular expression, a compile, or a registration list. Know how it fails: skip or fail, and which condition sets the exit status. The last matters most for oracle scripts, where a printed difference does not always produce a non-zero exit; the oracle page gives the case.
A review method for a big diff
- Read the campaign structure first.
git log --first-parent --merges <base>..<head>lists the integration story. For one mergeM:git rev-list --count M^1..M^2,git diff --shortstat M^1 M, andgit diff-tree --cc -r --name-only M, which lists the files the merge itself edited relative to both parents. Then open the plan that the branch name or IDs point to and compare its stated scope with the commits. - Map changed paths to owners. Group
git diff --numstatby module directory and look each owner up in Source ownership. Shared modules (modules/graphicstests and headers) mean every renderer is in scope even when the title names one. - Read the tests before the implementation. Which existing tests changed expectations, and does the message name a measurement (a real-XNA probe, a task ID) or merely a new result? Check where each test is registered: renderer gate lists such as
CNA_RENDERER_IS(…), the source filters incmake/UnitTests.cmake, and the skip convention. - Extract the invariants the change relies on (architectural invariants) and find a test for each: per-device GL contexts, compute bindings restored after a dispatch, the selection latch, ownership order (ownership map).
- Check comment and behaviour drift in the touched files. Header doc against
.cppcomment against code; prose counts against registrations and lists; task citations that resolve to nothing. - Look for hedged evidence. “Measured” against “reasoned”, “expected”, “should”, “not measured”; a claim with no command or test; one GPU, one driver, one date.
scripts/validate_direct2d_plan.pyis a model of the rule: a done row must cite evidence that exists. - Distrust “all tests pass” without a configuration. Ask for renderer, platform, audio option, generator, display, skips and known failures (reporting evidence honestly).
- Record what you could not verify (investigation record), including hardware you lack and numbers you could only cross-check.
Worked example: reading the head merge, 009d40f5
The snapshot is itself a merge: merge(OpenGL4ModernGraphics), 2026-09-24. The commands are those of the method; the numbers were counted from Git objects.
Structure
The first parent is b2a0a567, exactly the baseline that plans/plan_opengl4_modern_graphics.md records; the second is 725700d9 (GL4-0037), 24 commits later. The merge base equals the first parent, so the branch is a linear stack, and the merge tree is identical to the second parent: the merge commit edits nothing itself (git diff-tree --cc lists no files). Three earlier fold merges differ from both parents: 93ca4ffdf (Vulkan, 34 files), 0b046a8d0 (WebGPU, 13) and 1e8ec10e7 (software, 135); those merges are changes in their own right. The message claims “GL4-0001..GL4-0037”; the 24 commits carry 35 numbered IDs (0003 to 0037) in ranges plus two gate rows, while 0001 and 0002 exist only as plan rows.
Owners
git show --stat 009d40f5 and git diff --shortstat b2a0a567 009d40f5 report 180 files, +25,778 and −6,595 lines. By module: modules/graphics 87 files (30 tests, 54 examples and three new shared GL headers of 2,128 lines), modules/renderers/opengl4 42 (+18,412, −4,185), modules/graphics-ext 30 (+2,388), cmake 6, modules/renderers/easygl 5 (1,929 lines deleted), modules/content 4, plans 3, tools 2, docs 1. A merge titled for one renderer removes 1,929 lines from the family that serves five other identities, and edits the shared tests of every renderer: those two facts define its blast radius.
Checks that cost nothing
- Tests before implementation. Commit
80c158f36(GL4-0018) changes shared test expectations to rules “measured from the XNA assemblies” (citingSOFTWARE-204,-246,-216andGSC-0004) and adds OpenGL4 to the shared renderer gates: its message says 191 gates were extended, and the commit touches 56 files whose patch adds 290 lines that mention OpenGL4. The 191 was not recounted; sample the gate lists, because a list decides which renderers a test runs on or skips for, and a wrong entry silently changes what is tested. - “Moved verbatim”.
GlStockShaderSources.hppstates that its shader text is moved verbatim from EasyGL. Of the 1,869 non-blank lines the merge removes from EasyGL's renderer source and header, 1,835 (98.2 %) reappear, ignoring indentation, among the 2,055 lines added to the three new shared headers; the other 34 are declaration, qualifier and call-site edits (for example theEasyGLSurfaceStatemethods,CnaGlIblDeclandAdaptStockVertexShaderForOpenGL). That supports the text-level claim only; identical text can still compile differently in another namespace or include order. - Numbers in the message against registrations. “CnaGraphicsExtTests 798/44/120 before, 960/0/7 after” sums to 962 and 967, which are exactly the static test-definition counts under
modules/graphics-ext/tests/at the two parents. “407” OpenGL4 CTests equals 26 direct registrations, 346 generated EasyGL-corpus entries (EasyGLParityCorpus.cmake), 32 parity fixtures and, withCNA_CNAEXTon, three modern tests, counted inopengl4/examples/CMakeLists.txt.CnaGraphicsTestsis 2,833 + 57 = 2,890 against 2,906 static definitions undermodules/graphics/tests; configuration filters can explain the 16, the message does not.
What stays unverified
Every pass, fail and skip count, the zero GL errors, the sanitizer runs and the 3,000-cycle stress are CNA's records from one Radeon 780M through the private runner; none was reproduced, and the merge message itself says findings for EasyGL, Vulkan, WebGPU and the Wayland platform are “recorded in the ledger, not changed”. The renderer-side reading is on OpenGL4 internals.
Short checklist
- Which file decides the behaviour: source, active CMake, a registered test? Not a plan, note or comment.
- Which merge or campaign brought it in, did a fold merge edit files on its own, and which owners and renderers does it touch beyond the one in the title?
- Did any test expectation change, and is the reason a measurement or merely a new result?
- Which invariant does the change rely on and which test pins it; do header docs, comments and behaviour agree; do stated counts match a registry?
- Is each evidence claim measured, dated, and tied to a host and a configuration?
- Would the guard that covers this area run in your configuration, and would it fail on this change?
- What did you not verify, and who can?
Limits of this page
Nothing was built or run; every statement was checked by reading files and Git objects at 009d40f5. The drift table is a sample, percentages come from simple patterns, and the guard descriptions state registrations and script logic, not outcomes at this snapshot. Which files an assistant wrote is not recoverable from Git and is not claimed. The oracle-specific evidence rules are on Using the XNA oracle as evidence.
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- Project records as evidence: plans, ledgers, versions, handoffs and re-audits — How to use and write CNA's plans, handoffs, audits and gates as evidence: roles, stable task IDs, executable ledgers, version coordinates, defect-ledger rules and documentation re-audits.
Related pages
The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.
- Architecture
- Architecture: renderer registry
- Internals
- Renderer selection: when the latch closes · OpenGL4: what the modern-graphics campaign changed
- Maintainer workflow
- Source ownership lookup · Investigation record · Known uncertainty and history
- Tests and validation
- Reporting evidence honestly · Skip convention
- Reference
- Architectural invariants · Test target index