Project records as evidence: plans, ledgers, versions, handoffs and re-audits

CNA snapshot 009d40f5  ·  Deep Dives › Porting & project practice  ·  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. Records, gates and tests were read at 009d40f5 and sharp-runtime's database at the two named revisions; nothing was executed. The recommended practices are not enforced by CNA's tooling.

CNA's repository is rich in plans, handoffs, audits, remediation ledgers and integration records. That abundance is valuable and dangerous at once: an old plan can explain why code exists while being wrong about whether it exists today. This page is about practice rather than a subsystem: which record answers which question at 009d40f5, how CNA turns some records into executable checks, how the product and ABI versions sit beside task history, and how to keep a defect ledger and a documentation re-audit honest. The inventory of the artifacts themselves, with their counts and trust limits, is on reviewing large AI-generated areas; this page adds the rules for using and writing them.

Documents have roles, not equal authority

RecordRole at this snapshotRead it as
NEXT.mdA session and integration handoff of 6,743 lines in dated sections, newest first (the top one is the XNA-ENUM-001 type-surface closure of 2026-09-20), with years of earlier renderer, platform, C API, glTF and remediation history below; eleven topical NEXT*.md files sit beside itOperational history, not a normalised statement of current features
CHECKLIST.mdThe per-file porting rules: surface from the XNA reference assemblies, behaviour from FNA, every non-XNA member marked CNAEXT, tests for each public member or a stated host-dependent exceptionPolicy; compliance still has to be checked against code and gates
AUDIT.mdA 1,966-line XNA API class table with dated banner notes on top (for example a 2026-09-19 correction from the Spacewar sample, and the Inspector and Diagnostics extension notes)A claim corpus: a new banner does not refresh the older rows below it
plans/103 plan_*.md files (106 Markdown files in all) plus one in the singular plan/ directory; alpha.1 had 62 at the repository rootIntent, decisions and measurements with dates, not current status
REMEDIATION_INDEX.mdRoot-cause tasks derived from the per-file audits, each with its evidence and closureLeads and closed histories; the directory is marked a historical archive

The common failure is reading one of these for a question it cannot answer. Source audits generated before the August 2026 modularisation cite paths that later moved; a plan row marked done records that work was done, not that its test still describes the framework (CNA's own WebGPU plan records eight such failures); a checklist says what should be true. Each is evidence of intent or history. Current behaviour is answered by source, generated configuration and tests.

Stable identifiers preserve causality

Numbered tasks record rationale, experiments, owner decisions, defects discovered while doing the work, and the boundary of each follow-up. A stable identifier lets commits, tests, comments and later tasks point back to one decision: GLTF-245 is referred to from three plans (plan_gltf.md, plan_cnb.md, plan_cnj.md), and REMED-GFX-188, an SDL_GPU cube-target finding recorded inside another ticket's closure in the remediation index, is cited from example programs and their CMake file. When a named path, test or commit moves, update the forward pointer; never erase the old identifier.

A done marker is still a claim. CNA's C ABI release-gate record (RELEASE_GATE.md) is a worked history of this: one criterion was closed on 2026-08-16, reopened the same day by a merge, closed again, and then found to have been partly an illusion, because 73 mapping rules matched a whole header with .* so that 121 newly merged symbols inherited "implemented and tested" without any C route existing; the rules were then pinned to the stable identifiers they had been reviewed against. A later closure was recorded only after a separate task verified it "rather than reading the measurement". The committed gate record at this snapshot reads Not ready, with one unmet criterion: 468 public C++ symbols unmapped. Running the gate script against the same tree measures two unmet criteria (coverage and limitations, both stopped by the inventory's scope check) and its --check fails, so even this record had drifted from its own measurement.

When a task grows across independent renderers or failure modes, split it. In the same record, the reopened rows were bound by five sibling tasks (CBIND-051A to 051E) and a later pair (052A, 052B), so a partial fix never turned a broad row green and each child carried its own acceptance evidence. The parent becomes a tracking pointer.

Integration records need immutable inputs

Merging independently developed campaigns is where prose summaries fail most. A useful checkpoint packet for work of that scale contains:

  • the base and input commit SHAs, the role of each branch, and evidence that no hidden working-tree state was consumed;
  • the path-overlap analysis and the reason for the merge order;
  • the exact configure, build and test commands, with renderer and host identity;
  • machine-readable results, skip counts, logs, screenshots and generated manifests;
  • newly found defects kept separate from the requested change;
  • a clean-tree and diff check, and a handoff that names the next bounded step.

A sentence such as "all lanes passed" cannot replace the packet, especially when one lane was only compile-checked or one renderer ran under a substitute runtime. CNA keeps one such record set in the tree: integration/ holds the August 2026 renderer-integration campaign (a lane inventory, an order file that names the constraints which really ordered the lanes, a history policy with an archive tag before each adapted lane, a range-diff on every lane card and dropped commits recorded with their reason, 21 lane cards, seven batch stabilisation notes and a final reconciliation). Read it as dated evidence, not a live policy: its own addendum of 2026-08-09 says the external MetaGL and EasyGL history was later rewritten and the external archive tags are now unsigned, so the old external SHAs and signature claims there are not current pins. A merge commit's summary numbers belong to that merge; the site's worked reading of the snapshot's head merge shows what a packet lets a reviewer reconstruct (the head merge, 009d40f5).

Executable ledgers resist prose drift

If a record names a finite set that code can derive, derive it and compare. CNA does this in several places:

  • Known defects as tests. GltfKnownDefectTests.cpp is built to hold each proven glTF import defect as a test that is green while CNA is broken (at this snapshot none remains: its header says all eight audit defects are fixed, leaving only the ledger test and the GLTF-241 fix witness): each such test asserts that the spec-derived expectation is still not met and that the divergence is exactly the recorded one, so a partial fix fails with the fixture and task named. Converting a case to passing is a manifest change in tools/gltf_fixtures, never a change to the fixture's expected values, and the record stays in the corpus as the regression witness. A ledger test, EveryOpenDefectInTheCorpusLedgerHasAnExecutableTestHere, checks both directions: every open defect in the corpus ledger has a test, and no remediated defect keeps a "still broken" one. At this snapshot the open set is empty; D1 to D8 and GLTF-241 are recorded as remediated with the closing task of each.
  • Registries against registries. check_renderer_identities.py compares the public renderer enum, the CMake selection list and the runtime registry against one canonical table, and also checks the documents that state the count, because "a count written into prose is a fact with no owner". It follows each identity through to its descriptor accessor after an identity once passed both spelling lists while its runtime registry entry made configuration fail. RendererDescriptorGate.cmake compiles every registered family's descriptor in every configuration, taking its inventory from the registry itself, with one stated exception: the descriptors that include SDK headers (Direct3D 9, 11 and 12, and Vulkan when no Vulkan headers are installed) are left to their own family target, so a tree that selects none of them compiles them nowhere.
  • Status derived from rows. count_webgpu_plan_status.sh computes the WebGPU plan's summary from its row table so the headline count is never hand-maintained.

The general rule: check row uniqueness, the accepted status vocabulary, set equality, path existence and non-empty parsing, and keep negative fixtures so the validator can be shown to go red. Executable documents rot too: prose counts inside build files outlive the registry they describe (the site's drift table lists several, such as a probe file still describing an earlier identity count), and a gate that cannot find its input is not current evidence. Self-validation must include its input mapping, and a physical source move should update implementation, tests, validators and cited documents together.

Two version coordinates beside the task history

The product version comes from the root CMakeLists.txt: project(CNA VERSION 0.1.0) plus CNA_VERSION_PRERELEASE (alpha.1), joined into CNA_VERSION_STRING and generated into CNA/Version.hpp from Version.hpp.in, where CNA::getVersionString() and the CNA_VERSION_* macros expose it. The experimental C ABI has its own version in abi.h (0.29.0 at this snapshot), which moves with ABI changes and not with product tags, as releasing.md states. A release record is complete only when it names both where the C surface is in scope. The snapshot documented here still reports 0.1.0-alpha.1 and is not a release; pre-1.0 SemVer identifies a state and signals prerelease status without promising a stable API. Task identifiers stay the causal ledger underneath: a task answers why and how, the tag answers which immutable product state. The release procedure is making a release.

A planning database makes scope measurable

sharp-runtime keeps its coarse classifications and concrete tickets in a tracked SQLite file, plan.sqlite3, with a task and a ticket table. The numbers only mean something with their revision and their query:

sharp-runtime revisiontask rowsby statusoutofscope=1ticket rows
f827a6c5 (2026-08-10)16,2011,082 ported, 14,979 ignored, 140 ignore1182,183 (2,073 done, 67 blocked, 29 todo, 10 needs_user, 4 wontfix)
41b918c9 (2026-09-20, next)16,2071,087 ported, 14,980 ignored, 140 ignore1182,417 (2,409 done, 3 blocked, 5 wontfix)
sqlite3 plan.sqlite3 'SELECT status, COUNT(*) FROM task GROUP BY status;'
sqlite3 plan.sqlite3 'SELECT COUNT(*) FROM ticket;'

The denominator matters more than the ratio. The database deliberately classifies vast areas of .NET that sharp-runtime will never implement, so "1,087 ported" counts rows with a working C++ counterpart under this schema; it is not a 6.7 % behavioural parity with the whole base class library. The status vocabulary itself is uneven (ignore and ignored coexist), which is one more reason to quote the query with the number. Doxygen @note Status: tags in headers are secondary human hints that can drift from the database. How sharp-runtime audits its own claims is on sharp-runtime parity and verification.

Separate observations, inferences and decisions

A robust task record keeps three kinds of statement apart. An observation is directly reproducible ("this generated gate reports 468 unmapped public symbols"). An inference connects evidence ("the gate reopened because a merge added public headers faster than routes were bound"). A decision sets policy: on 2026-08-28 the project owner ruled that binding the CNB content headers was not that pass's work, and the gate record keeps every such row planned with an open task rather than marking it not applicable, because "deferring work and deciding it has no C form are different claims". Mixing the three lets plans outlive their premises: a decision can stay valid after the cited defect moves, an observation can become false after a fix, and an inference needs revisiting when a new control run appears. Marking the category makes the next re-audit cheaper.

Handoff is part of implementation

An effective handoff states what is complete, what was verified and how, what remains, which repositories are read-only, and which actions need external approval. It lists failed approaches whose repetition would waste time and names the documents known to be stale so they are not used as status sources. It does not paste an ever-growing transcript into one file: CNA's AGENTS.md asks for NEXT.md and AUDIT.md to be updated at the end of each task, yet both were last changed on 2026-09-20, 194 commits before this snapshot, so the handoff layer itself needs a date check before use.

The practical authority order when records disagree:

  1. current source and generated configuration;
  2. executable tests and retained artifacts;
  3. accepted integration and remediation records;
  4. plans, audits, READMEs and comments, as context.

Comments often preserve the best explanation and the worst status. Durable practice keeps both visible by linking every explanation to something a later maintainer can rerun.

Keeping a defect ledger honest

The same discipline applies to any list of known defects, including the one this site maintains. An identifier stays open until current source plus a regression test or reproducer proves it fixed, or until the public contract it concerned is genuinely gone; "the code looks different now" is not closure. A partial fix narrows the entry to the surviving case instead of closing it. New confirmed defects get new identifiers above the current maximum, so numbering never hides a deletion. Unsupported features and missing evidence are not defects: they belong in separate categories (functional gap, platform limitation, verification gap), each stated as a scoped fact. CNA's glTF ledger above is this rule made executable: a remediated record is kept as a witness, and the test fails if the bookkeeping and the code disagree in either direction.

Re-auditing documentation against a later snapshot

Documentation written against one commit drifts as the code moves. A re-audit that stays trustworthy follows a fixed order:

  1. compute the delta from the pinned commit to the new one, read-only, without touching the working checkout;
  2. inventory the first-parent campaigns and changed paths, and treat every changed path a page cites as a mandatory re-read (an unchanged path only proves the file is identical, not that the old claim was right);
  3. re-derive every registry-backed fact from source (renderer identities, capabilities, platform and audio implementations, the ABI version, the API census) instead of editing numbers by hand;
  4. carry forward every open defect identifier and account for each one that closes;
  5. scan for content about removed functionality, and keep the previous, published state intact until the new one validates.

For this site the concrete procedure, with its scripts and validators, is updating libcna.com after CNA changes; the remaining work at this snapshot, re-derived from source, is on the detailed roadmap.

Evidence and limits

Checked by reading, at 009d40f5: NEXT.md, AUDIT.md, CHECKLIST.md, the plan directories, RELEASE_GATE.md, GltfKnownDefectTests.cpp, check_renderer_identities.py, RendererDescriptorGate.cmake, the root CMakeLists.txt and releasing.md. The sharp-runtime figures were computed with the two queries shown against plan.sqlite3 as committed at each named revision. Plan and commit counts are file and history counts made for this page; nothing was built or executed. The checkpoint packet, the three-way statement classification and the authority order are recommended practice, not rules CNA's tooling enforces.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.