CNA-BUG-245: misc/cnj.md and xnb.md say the content manifest services ResolveAssetPath's existence checks; ContentManager still probes the live filesystem on every Load
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. Nothing on this page was executed unless the Evidence section says so.
CNA's CNJ and XNB design notes describe the startup content-manifest scan as the cache that replaces ResolveAssetPath's std::filesystem::exists calls, but at TARGET the manifest is an introspection snapshot that Load<T> never consults.
- Identifier
CNA-BUG-245- Category
- Bug
- Subsystem
- Documentation & release tooling
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- ContentManager::Load<T> / ResolveAssetPath resolution and ContentManager::GetContentManifest / RefreshContentManifest, as described by misc/cnj.md and xnb.md
Expected behaviour
CNA's design notes describe the resolution path as implemented. ContentManager.hpp itself records the current contract at contentManifest_: the manifest is “NOT yet consulted by ResolveAssetPath()/Load<T>()'s own exists()-based resolution”, and wiring it in is “deliberately deferred” so that ContentManager keeps noticing a file the instant it is written.
Actual behaviour at TARGET
misc/cnj.md (the resolution-order rationale) says the extra failed .cnj existence check is “negligible, and now serviced by the content-manifest cache (see xnb.md's ‘Content manifest’ section) rather than a raw stat call”, and a later bullet calls the scan “an internal performance cache”. xnb.md (“Content manifest: startup Content-root scan”) says the manifest “replaces” ResolveAssetPath's per-candidate std::filesystem::exists() calls with one upfront walk, and that a file added after the scan is not found until RefreshContentManifest(). At TARGET, ResolveAssetPath (inline in ContentManager.hpp) calls std::filesystem::exists for the literal path, for the .cnj candidate and for each extension candidate, each after ResolveExistingAssetPath's live case-insensitive directory walk (ContentManager.cpp); the .xnb/.cnb probes before it use the same walk. The manifest is built only by GetContentManifest()/RefreshContentManifest(). Read together, the notes imply that Load<T> would miss a file added after the scan; in fact Load<T> finds it immediately, and only GetContentManifest() is a snapshot.
Source locations
misc/cnj.md— resolution-order rationale paragraph and the content-manifest scan bulletxnb.md— Content manifest: startup Content-root scan, item 1 (internal performance cache)modules/content/include/Microsoft/Xna/Framework/Content/ContentManager.hpp— contentManifest_ comment; ResolveAssetPath; GetContentManifest; RefreshContentManifestmodules/content/src/Xna/ContentManager.cpp— ResolveExistingAssetPath (live case-insensitive walk); RefreshContentManifestmodules/content/tests/Microsoft/Xna/Framework/Content/ContentManagerManifestTests.cpp— manifest tests; none ties Load to the manifest
Evidence
Checked by reading at 009d40f5; not executed. Read the two design notes, ContentManager.hpp (contentManifest_ comment, ResolveAssetPath, the .xnb/.cnb probes) and ContentManager::ResolveExistingAssetPath / RefreshContentManifest. The header comment and the code agree with each other and contradict both notes. The companion claim of a global cnjVersion ceiling of 1 is no longer made: misc/cnj.md documents per-type ceilings. None of these files changed between the earlier recorded revision and TARGET.
Focused reproduction
No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.
Current tests
ContentManagerManifestTests.cpp covers the manifest itself (native/.cnj/.xnb discovery, grouping, reader inventory, malformed files, RefreshContentManifest picking up new files). Nothing checks the design notes, and no test relates Load<T> to the manifest, which is correct for the current contract.
Regression test
Documentation fix: state in both notes that the manifest is an introspection snapshot and that Load<T> resolves against the live filesystem. If the manifest is later wired into ResolveAssetPath, a test that writes a file after the scan and loads it pins the chosen freshness rule.
Blast radius
Readers and maintainers of the content pipeline who rely on the design notes for resolution cost or freshness; no runtime behaviour is affected.
Workaround
Trust the contentManifest_ comment in ContentManager.hpp and the site's resolution rules: Load<T> never consults the manifest.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- ContentManager: core API
- Deep dives
- ContentManager resolution rules: the manifest · ContentManager resolution rules: existence selects
- Known issues
- Bug index