diff options
| author | Valentin Popov <valentin@popov.link> | 2026-07-03 20:44:26 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-07-03 20:44:26 +0300 |
| commit | f34c422b20fd1fc1b4bf71ebf448b3c42f104226 (patch) | |
| tree | c6478b2e605eecbab2a29d645d227ec8befa61ca | |
| parent | a89bd9af2aa6a4c85a7dce5cbc61eb38c6a91f49 (diff) | |
| download | fparkan-f34c422b20fd1fc1b4bf71ebf448b3c42f104226.tar.xz fparkan-f34c422b20fd1fc1b4bf71ebf448b3c42f104226.zip | |
test: realign stage 2 acceptance coverage
| -rw-r--r-- | apps/fparkan-cli/src/main.rs | 43 | ||||
| -rw-r--r-- | crates/fparkan-assets/src/lib.rs | 346 | ||||
| -rw-r--r-- | crates/fparkan-runtime/src/lib.rs | 56 | ||||
| -rw-r--r-- | fixtures/acceptance/coverage.tsv | 41 | ||||
| -rw-r--r-- | fixtures/acceptance/stage_0_2_roadmap.md | 41 |
5 files changed, 482 insertions, 45 deletions
diff --git a/apps/fparkan-cli/src/main.rs b/apps/fparkan-cli/src/main.rs index e96f787..3e40f41 100644 --- a/apps/fparkan-cli/src/main.rs +++ b/apps/fparkan-cli/src/main.rs @@ -255,7 +255,12 @@ fn prototype_inspect_json( textures: report.texture_resolved_count, lightmaps: report.lightmap_resolved_count, is_success: report.is_success(), - failures: report.failures.iter().take(16).map(graph_failure_output).collect(), + failures: report + .failures + .iter() + .take(16) + .map(graph_failure_output) + .collect(), }) } @@ -366,9 +371,7 @@ fn serialize_json<T: Serialize>(value: &T) -> Result<String, String> { serde_json::to_string(value).map_err(|err| err.to_string()) } -fn graph_failure_output( - failure: &fparkan_prototype::PrototypeGraphFailure, -) -> GraphFailureOutput { +fn graph_failure_output(failure: &fparkan_prototype::PrototypeGraphFailure) -> GraphFailureOutput { GraphFailureOutput { root_index: failure.root_index, edge: prototype_graph_edge_label(failure.edge), @@ -472,4 +475,36 @@ mod tests { "{\"schema_version\":\"fparkan-prototype-inspect-v1\",\"key\":\"root\",\"roots\":1,\"node_count\":0,\"edge_count\":0,\"prototype_requests\":1,\"resolved\":1,\"unit_references\":0,\"unit_components\":0,\"direct_references\":1,\"wear_requests\":0,\"wear\":0,\"materials\":0,\"textures\":0,\"lightmaps\":0,\"is_success\":true,\"failures\":[]}" ); } + + #[test] + fn mission_graph_json_has_canonical_field_order() { + let json = serialize_json(&MissionGraphOutput { + schema_version: MISSION_GRAPH_SCHEMA, + mission: "MISSIONS/Autodemo.00/data.tma".to_string(), + objects: 2, + paths: 3, + clans: 4, + extras: 5, + roots: 6, + node_count: 7, + edge_count: 8, + direct_references: 9, + unit_references: 10, + unit_components: 11, + prototype_requests: 12, + wear_requests: 13, + wear: 14, + materials: 15, + textures: 16, + lightmaps: 17, + is_success: true, + failures: 0, + }) + .expect("serialize"); + + assert_eq!( + json, + "{\"schema_version\":\"fparkan-mission-graph-v1\",\"mission\":\"MISSIONS/Autodemo.00/data.tma\",\"objects\":2,\"paths\":3,\"clans\":4,\"extras\":5,\"roots\":6,\"node_count\":7,\"edge_count\":8,\"direct_references\":9,\"unit_references\":10,\"unit_components\":11,\"prototype_requests\":12,\"wear_requests\":13,\"wear\":14,\"materials\":15,\"textures\":16,\"lightmaps\":17,\"is_success\":true,\"failures\":0}" + ); + } } diff --git a/crates/fparkan-assets/src/lib.rs b/crates/fparkan-assets/src/lib.rs index 460b3c6..8fc2501 100644 --- a/crates/fparkan-assets/src/lib.rs +++ b/crates/fparkan-assets/src/lib.rs @@ -771,7 +771,11 @@ pub fn extend_graph_report_with_visual_dependencies<R: ResourceRepository>( wear_node_id, fparkan_prototype::PrototypeGraphEdgeKind::MeshToWear, PrototypeGraphRequiredness::Required, - Some(provenance_for_resource(root_index, mesh_parent_edge, &wear_key)), + Some(provenance_for_resource( + root_index, + mesh_parent_edge, + &wear_key, + )), &mut next_edge, ); report.material_slot_count += table.entries.len(); @@ -937,7 +941,9 @@ fn push_graph_resource_node( *next_node = (*next_node).saturating_add(1); graph .nodes - .push(fparkan_prototype::PrototypeGraphNode::resource(kind, resource, id)); + .push(fparkan_prototype::PrototypeGraphNode::resource( + kind, resource, id, + )); id } @@ -952,14 +958,16 @@ fn push_graph_edge( ) -> fparkan_prototype::PrototypeGraphEdgeId { let id = fparkan_prototype::PrototypeGraphEdgeId(*next_edge); *next_edge = (*next_edge).saturating_add(1); - graph.edges.push(fparkan_prototype::PrototypeGraphEdgeInstance { - id, - from, - to, - kind, - requiredness, - provenance, - }); + graph + .edges + .push(fparkan_prototype::PrototypeGraphEdgeInstance { + id, + from, + to, + kind, + requiredness, + provenance, + }); id } @@ -1144,11 +1152,8 @@ fn prepare_visual_with_repository_internal<R: ResourceRepository>( }); for texture in texture_requests { - let prepared_texture = prepare_texture( - repository, - &texture, - PreparedTextureUsage::Diffuse, - )?; + let prepared_texture = + prepare_texture(repository, &texture, PreparedTextureUsage::Diffuse)?; texture_ids.push(prepared_texture.id); prepared_textures.push(prepared_texture); texture_count += 1; @@ -1664,7 +1669,10 @@ mod tests { }; assert_ne!(stable_visual_id(&first), stable_visual_id(&second)); - assert_ne!(prepared_visual_signature(&first), prepared_visual_signature(&second)); + assert_ne!( + prepared_visual_signature(&first), + prepared_visual_signature(&second) + ); } #[test] @@ -1781,6 +1789,222 @@ mod tests { } #[test] + fn graph_visual_dependency_edges_preserve_root_and_parent_provenance() { + let mesh_key = ResourceKey { + archive: parse_path("static.rlb").expect("archive"), + name: resource_name(b"tree.msh"), + type_id: Some(0x4853_454D), + }; + let mat0 = mat0_with_texture(b"TEX_A"); + let texm = texm_payload(); + let lightmap_texm = texm_payload(); + let repo = repository_with_archives_meta(&[ + ( + "static.rlb", + &[TestNresEntry { + name: b"tree.wea", + payload: b"1\n0 MAT_A\n\nLIGHTMAPS\n1\n0 LM_A\n", + type_id: WEAR_KIND, + attr2: 0, + }], + ), + ( + "material.lib", + &[TestNresEntry { + name: b"MAT_A", + payload: &mat0, + type_id: MAT0_KIND, + attr2: 0, + }], + ), + ( + TEXTURES_ARCHIVE, + &[TestNresEntry { + name: b"TEX_A", + payload: &texm, + type_id: 0, + attr2: 0, + }], + ), + ( + LIGHTMAP_ARCHIVE, + &[TestNresEntry { + name: b"LM_A", + payload: &lightmap_texm, + type_id: 0, + attr2: 0, + }], + ), + ]); + let prototype = EffectivePrototype { + key: fparkan_prototype::PrototypeKey(resource_name(b"tree")), + geometry: PrototypeGeometry::Mesh(mesh_key), + source: fparkan_prototype::PrototypeSource::DirectArchive, + dependencies: Vec::new(), + }; + let mut graph = prototype_graph_for_mesh(&prototype); + let mut report = PrototypeGraphReport { + root_count: 1, + direct_reference_count: 1, + resolved_count: 1, + mesh_dependency_count: 1, + ..PrototypeGraphReport::default() + }; + + extend_graph_report_with_visual_dependencies( + &repo, + &mut report, + &mut graph, + std::slice::from_ref(&prototype), + ); + + let wear_edge = graph + .edges + .iter() + .find(|edge| edge.kind == fparkan_prototype::PrototypeGraphEdgeKind::MeshToWear) + .expect("wear edge"); + let material_edge = graph + .edges + .iter() + .find(|edge| edge.kind == fparkan_prototype::PrototypeGraphEdgeKind::WearToMaterial) + .expect("material edge"); + let texture_edge = graph + .edges + .iter() + .find(|edge| edge.kind == fparkan_prototype::PrototypeGraphEdgeKind::MaterialToTexture) + .expect("texture edge"); + let lightmap_edge = graph + .edges + .iter() + .find(|edge| edge.kind == fparkan_prototype::PrototypeGraphEdgeKind::WearToLightmap) + .expect("lightmap edge"); + + assert_eq!( + wear_edge + .provenance + .as_ref() + .expect("wear provenance") + .parent_edge, + Some(fparkan_prototype::PrototypeGraphEdgeId(1)) + ); + assert_eq!( + material_edge + .provenance + .as_ref() + .expect("material provenance") + .parent_edge, + Some(wear_edge.id) + ); + assert_eq!( + texture_edge + .provenance + .as_ref() + .expect("texture provenance") + .parent_edge, + Some(material_edge.id) + ); + assert_eq!( + lightmap_edge + .provenance + .as_ref() + .expect("lightmap provenance") + .parent_edge, + Some(wear_edge.id) + ); + assert!(graph + .edges + .iter() + .filter_map(|edge| edge.provenance.as_ref()) + .all(|provenance| provenance.root_index == 0)); + } + + #[test] + fn prepare_single_visual_mission_assets_materialize_model_wear_material_and_texture_payloads() { + let mesh_key = ResourceKey { + archive: parse_path("static.rlb").expect("archive"), + name: resource_name(b"tree.msh"), + type_id: Some(0x4853_454D), + }; + let msh = minimal_model_archive(); + let mat0 = mat0_with_texture(b"TEX_A"); + let texm = texm_payload(); + let lightmap_texm = texm_payload(); + let repo = repository_with_archives_meta(&[ + ( + "static.rlb", + &[ + TestNresEntry { + name: b"tree.msh", + payload: &msh, + type_id: 0x4853_454D, + attr2: 0, + }, + TestNresEntry { + name: b"tree.wea", + payload: b"1\n0 MAT_A\n\nLIGHTMAPS\n1\n0 LM_A\n", + type_id: WEAR_KIND, + attr2: 0, + }, + ], + ), + ( + "material.lib", + &[TestNresEntry { + name: b"MAT_A", + payload: &mat0, + type_id: MAT0_KIND, + attr2: 0, + }], + ), + ( + TEXTURES_ARCHIVE, + &[TestNresEntry { + name: b"TEX_A", + payload: &texm, + type_id: 0, + attr2: 0, + }], + ), + ( + LIGHTMAP_ARCHIVE, + &[TestNresEntry { + name: b"LM_A", + payload: &lightmap_texm, + type_id: 0, + attr2: 0, + }], + ), + ]); + let prototype = EffectivePrototype { + key: fparkan_prototype::PrototypeKey(resource_name(b"tree")), + geometry: PrototypeGeometry::Mesh(mesh_key), + source: fparkan_prototype::PrototypeSource::DirectArchive, + dependencies: Vec::new(), + }; + + let assets = prepare_mission_assets_with_repository( + &repo, + std::slice::from_ref(&(0..1)), + std::slice::from_ref(&prototype), + ) + .expect("prepared mission assets"); + + assert_eq!(assets.models.len(), 1); + assert_eq!(assets.wears.len(), 1); + assert_eq!(assets.materials.len(), 1); + assert_eq!(assets.textures.len(), 2); + assert_eq!(assets.visuals.len(), 1); + assert_eq!(assets.object_visuals, vec![vec![assets.visuals[0].id]]); + + let visual = &assets.visuals[0]; + assert_eq!(visual.model_id, Some(assets.models[0].id)); + assert_eq!(visual.wear_id, Some(assets.wears[0].id)); + assert_eq!(visual.material_ids, vec![assets.materials[0].id]); + assert_eq!(visual.texture_ids.len(), 1); + assert_eq!(visual.lightmap_ids.len(), 1); + } + + #[test] fn graph_report_uses_strict_texture_archive_policy() { let mesh_key = ResourceKey { archive: parse_path("static.rlb").expect("archive"), @@ -1843,7 +2067,10 @@ mod tests { assert_eq!(report.texture_request_count, 1); assert_eq!(report.texture_resolved_count, 0); assert_eq!(report.failures.len(), 1); - assert_eq!(report.failures[0].edge, PrototypeGraphEdge::MaterialToTexture); + assert_eq!( + report.failures[0].edge, + PrototypeGraphEdge::MaterialToTexture + ); } #[test] @@ -1937,7 +2164,10 @@ mod tests { let mut vfs = MemoryVfs::default(); for (archive, entries) in archives { let path = parse_path(archive).expect("archive path"); - vfs.insert(path, Arc::from(build_nres_with_meta(entries).into_boxed_slice())); + vfs.insert( + path, + Arc::from(build_nres_with_meta(entries).into_boxed_slice()), + ); } CachedResourceRepository::new(Arc::new(vfs)) } @@ -1964,6 +2194,81 @@ mod tests { bytes } + fn minimal_model_archive() -> Vec<u8> { + struct MshEntry<'a> { + type_id: u32, + attr3: u32, + name: &'a [u8], + payload: &'a [u8], + } + + let entries = [ + MshEntry { + type_id: 1, + attr3: 38, + name: b"Res1", + payload: &[], + }, + MshEntry { + type_id: 2, + attr3: 0, + name: b"Res2", + payload: &[0; 0x8c], + }, + MshEntry { + type_id: 3, + attr3: 0, + name: b"Res3", + payload: &[], + }, + MshEntry { + type_id: 6, + attr3: 0, + name: b"Res6", + payload: &[], + }, + MshEntry { + type_id: 13, + attr3: 0, + name: b"Res13", + payload: &[], + }, + ]; + + let mut out = vec![0; 16]; + let mut offsets = Vec::with_capacity(entries.len()); + for entry in &entries { + offsets.push(u32::try_from(out.len()).expect("offset")); + out.extend_from_slice(entry.payload); + let padding = (8 - (out.len() % 8)) % 8; + out.resize(out.len() + padding, 0); + } + let mut order: Vec<usize> = (0..entries.len()).collect(); + order.sort_by(|left, right| entries[*left].name.cmp(entries[*right].name)); + for (idx, entry) in entries.iter().enumerate() { + push_u32(&mut out, entry.type_id); + push_u32(&mut out, 0); + push_u32(&mut out, 0); + push_u32( + &mut out, + u32::try_from(entry.payload.len()).expect("payload"), + ); + push_u32(&mut out, entry.attr3); + let mut name_raw = [0; 36]; + let len = name_raw.len().saturating_sub(1).min(entry.name.len()); + name_raw[..len].copy_from_slice(&entry.name[..len]); + out.extend_from_slice(&name_raw); + push_u32(&mut out, offsets[idx]); + push_u32(&mut out, u32::try_from(order[idx]).expect("sort index")); + } + out[0..4].copy_from_slice(b"NRes"); + out[4..8].copy_from_slice(&0x100_u32.to_le_bytes()); + out[8..12].copy_from_slice(&u32::try_from(entries.len()).expect("count").to_le_bytes()); + let total_size = u32::try_from(out.len()).expect("total size"); + out[12..16].copy_from_slice(&total_size.to_le_bytes()); + out + } + fn prototype_graph_for_mesh(prototype: &EffectivePrototype) -> PrototypeGraph { let root_node = fparkan_prototype::PrototypeGraphNode::root( fparkan_prototype::PrototypeKey(prototype.key.0.clone()), @@ -2055,7 +2360,10 @@ mod tests { push_u32(&mut out, entry.type_id); push_u32(&mut out, 0); push_u32(&mut out, entry.attr2); - push_u32(&mut out, u32::try_from(entry.payload.len()).expect("payload")); + push_u32( + &mut out, + u32::try_from(entry.payload.len()).expect("payload"), + ); push_u32(&mut out, 0); let mut name_raw = [0; 36]; let len = name_raw.len().saturating_sub(1).min(entry.name.len()); diff --git a/crates/fparkan-runtime/src/lib.rs b/crates/fparkan-runtime/src/lib.rs index d10647d..6a4e478 100644 --- a/crates/fparkan-runtime/src/lib.rs +++ b/crates/fparkan-runtime/src/lib.rs @@ -1115,6 +1115,50 @@ mod tests { } #[test] + #[ignore = "requires local testdata corpus"] + fn selected_is_and_is2_missions_preserve_runtime_object_drafts() { + for case in [ + ("IS", "MISSIONS/Autodemo.00/data.tma"), + ("IS2", "MISSIONS/Autodemo.00/data.tma"), + ] { + let root = local_testdata_root(case.0); + let vfs: Arc<dyn Vfs> = Arc::new(DirectoryVfs::new(root)); + let mut engine = create( + EngineConfig { + mode: EngineMode::Headless, + }, + EngineServices::new(vfs), + ) + .expect("engine"); + let loaded = load_mission( + &mut engine, + MissionRequest { + key: case.1.to_string(), + }, + ) + .expect("load mission"); + let drafts = loaded_mission_object_drafts(&engine).expect("object drafts"); + let assets = loaded_mission_assets(&engine).expect("mission assets"); + + assert_eq!(drafts.len(), loaded.object_count); + assert!(drafts.iter().any(|draft| !draft.visual_ids.is_empty())); + for (index, draft) in drafts.iter().enumerate() { + assert_eq!( + draft.original_id, + u32::try_from(index).ok().map(OriginalObjectId) + ); + assert_eq!(draft.visual_ids, assets.visuals_for_object(index)); + assert!(draft.position.iter().all(|component| component.is_finite())); + assert!(draft + .orientation_raw + .iter() + .all(|component| component.is_finite())); + assert!(draft.scale.iter().all(|component| component.is_finite())); + } + } + } + + #[test] #[ignore = "requires licensed corpus"] fn licensed_corpora_load_all_mission_foundations() { let part1 = load_all(&licensed_root("IS")); @@ -1326,6 +1370,18 @@ mod tests { root } + fn local_testdata_root(name: &str) -> PathBuf { + let root = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../testdata") + .join(name); + assert!( + root.is_dir(), + "local testdata root is missing: {}", + root.display() + ); + root + } + #[derive(Clone, Copy)] enum DenyRule { Suffix(&'static str), diff --git a/fixtures/acceptance/coverage.tsv b/fixtures/acceptance/coverage.tsv index 16e80cf..76a810b 100644 --- a/fixtures/acceptance/coverage.tsv +++ b/fixtures/acceptance/coverage.tsv @@ -207,6 +207,17 @@ S2-GRAPH-003 covered cargo test -p fparkan-assets --offline repository_plan_dedu S2-GRAPH-004 covered cargo test -p fparkan-prototype --offline graph_report_records_resolved_roots_and_failures S2-GRAPH-005 covered cargo test -p fparkan-cli --offline prototype_graph_json_has_canonical_field_order S2-GRAPH-006 covered cargo test -p fparkan-prototype --offline graph_report_records_resolved_roots_and_failures +S2-GRAPH-EDGE-001 covered cargo test -p fparkan-assets --offline graph_materializes_visual_dependency_nodes_and_edges +S2-GRAPH-PROV-001 covered cargo test -p fparkan-assets --offline graph_visual_dependency_edges_preserve_root_and_parent_provenance +S2-UNIT-EMPTY-001 covered cargo test -p fparkan-prototype --offline empty_unit_dat_resolves_as_zero_component_root +S2-ASSET-MODEL-001 covered cargo test -p fparkan-assets --offline prepare_single_visual_mission_assets_materialize_model_wear_material_and_texture_payloads +S2-ASSET-WEAR-001 covered cargo test -p fparkan-assets --offline prepare_single_visual_mission_assets_materialize_model_wear_material_and_texture_payloads +S2-ASSET-MATERIAL-001 covered cargo test -p fparkan-assets --offline prepare_single_visual_mission_assets_materialize_model_wear_material_and_texture_payloads +S2-ASSET-TEXM-001 covered cargo test -p fparkan-assets --offline prepare_single_visual_mission_assets_materialize_model_wear_material_and_texture_payloads +S2-RUNTIME-DRAFT-001 covered local testdata/IS and testdata/IS2; cargo test -p fparkan-runtime --offline -- --ignored selected_is_and_is2_missions_preserve_runtime_object_drafts +S2-CLI-JSON-001 covered cargo test -p fparkan-cli --offline prototype_graph_json_has_canonical_field_order mission_graph_json_has_canonical_field_order +S2-LICENSED-MISSION-P1-001 covered local testdata/IS; cargo test -p fparkan-runtime --offline -- --ignored selected_is_and_is2_missions_preserve_runtime_object_drafts +S2-LICENSED-MISSION-P2-001 covered local testdata/IS2; cargo test -p fparkan-runtime --offline -- --ignored selected_is_and_is2_missions_preserve_runtime_object_drafts S2-PROP-001 covered cargo test -p fparkan-prototype --offline generated_acyclic_prototype_graph_resolves_deterministically S2-FUZZ-001 covered cargo test -p fparkan-prototype --offline arbitrary_unit_and_registry_bytes_are_bounded_and_panic_free L3-P1-MSH-001 covered cargo test -p fparkan-msh --offline licensed_corpus_msh_assets_validate @@ -219,8 +230,8 @@ L3-P1-WEAR-001 covered cargo test -p fparkan-material --offline licensed_corpus_ L3-P2-WEAR-001 covered cargo test -p fparkan-material --offline licensed_corpus_mat0_and_wear_parse L3-P1-ASSET-001 covered cargo test -p fparkan-runtime --offline licensed_corpora_load_all_mission_foundations L3-P2-ASSET-001 covered cargo test -p fparkan-runtime --offline licensed_corpora_load_all_mission_foundations -L3-P1-CAPTURE-001 covered cargo test -p fparkan-game --offline selected_is_and_is2_missions_produce_approved_render_captures -L3-P2-CAPTURE-001 covered cargo test -p fparkan-game --offline selected_is_and_is2_missions_produce_approved_render_captures +S2-PLANNING-CAPTURE-P1-001 covered cargo test -p fparkan-game --offline selected_is_and_is2_missions_produce_approved_render_captures +S2-PLANNING-CAPTURE-P2-001 covered cargo test -p fparkan-game --offline selected_is_and_is2_missions_produce_approved_render_captures S3-WEAR-001 covered cargo test -p fparkan-material --offline wear_preserves_legacy_id_but_selects_by_index S3-WEAR-002 covered cargo test -p fparkan-material --offline wear_requires_declared_rows S3-WEAR-003 covered cargo test -p fparkan-material --offline wear_preserves_legacy_id_but_selects_by_index @@ -271,15 +282,23 @@ S3-MAT-RESOLVE-002 covered cargo test -p fparkan-material --offline resolve_mate S3-MAT-RESOLVE-003 covered cargo test -p fparkan-material --offline resolve_material_uses_first_entry_only_after_missing_default S3-MAT-RESOLVE-004 covered cargo test -p fparkan-material --offline resolve_material_empty_texture_means_untextured S3-MAT-RESOLVE-005 covered cargo test -p fparkan-material --offline resolve_material_without_lightmap_keeps_lightmap_absent -S3-RENDER-001 covered cargo test -p fparkan-render --offline one_snapshot_draw_produces_one_draw_command -S3-RENDER-002 covered cargo test -p fparkan-render --offline material_index_maps_through_resolved_material_slots -S3-RENDER-003 covered cargo test -p fparkan-render --offline node_transform_is_retained -S3-RENDER-004 covered cargo test -p fparkan-render --offline command_order_uses_phase_then_stable_key -S3-RENDER-005 covered cargo test -p fparkan-render --offline command_capture_independent_of_snapshot_construction_order -S3-RENDER-006 covered cargo test -p fparkan-render --offline invalid_range_returns_contextual_error -S3-RENDER-007 covered cargo test -p fparkan-render --offline capture_is_stable -S3-RENDER-008 covered cargo test -p fparkan-render --offline recording_backend_stores_captures -S3-RENDER-009 covered cargo xtask policy +S2-PLANNING-RENDER-001 covered cargo test -p fparkan-render --offline one_snapshot_draw_produces_one_draw_command +S2-PLANNING-RENDER-002 covered cargo test -p fparkan-render --offline material_index_maps_through_resolved_material_slots +S2-PLANNING-RENDER-003 covered cargo test -p fparkan-render --offline node_transform_is_retained +S2-PLANNING-RENDER-004 covered cargo test -p fparkan-render --offline command_order_uses_phase_then_stable_key +S2-PLANNING-RENDER-005 covered cargo test -p fparkan-render --offline command_capture_independent_of_snapshot_construction_order +S2-PLANNING-RENDER-006 covered cargo test -p fparkan-render --offline invalid_range_returns_contextual_error +S2-PLANNING-RENDER-007 covered cargo test -p fparkan-render --offline capture_is_stable +S2-PLANNING-RENDER-008 covered cargo test -p fparkan-render --offline recording_backend_stores_captures +S2-PLANNING-RENDER-009 covered cargo xtask policy +S3-VK-MESH-UPLOAD-001 blocked awaits Stage 3 Vulkan asset renderer and GPU upload path +S3-VK-TEXM-UPLOAD-001 blocked awaits Stage 3 Vulkan texture upload path +S3-VK-DESCRIPTOR-001 blocked awaits Stage 3 descriptor set contract implementation +S3-VK-PIPELINE-001 blocked awaits Stage 3 graphics pipeline key and cache implementation +S3-VK-DRAW-MODEL-001 blocked awaits Stage 3 real Vulkan indexed model draw path +S3-VK-DRAW-TERRAIN-001 blocked awaits Stage 3 terrain Vulkan draw path +S3-VK-PIXEL-CAPTURE-001 blocked awaits Stage 3 fixed-camera pixel capture approval flow +S3-VK-VALIDATION-001 blocked awaits Stage 3 validation-clean GPU frame execution S3-GL-001 omitted permanent macOS Desktop GL 3.3 adapter is not implemented; historical CGL probe is retained as external evidence only S3-GL-002 omitted outside the current macOS-focused goal scope; GLES2 remains documented for portable/non-macOS targets S3-GL-003 blocked legacy fparkan-render-gl adapter removed while Vulkan renderer path is being brought in as the stage-3 backend diff --git a/fixtures/acceptance/stage_0_2_roadmap.md b/fixtures/acceptance/stage_0_2_roadmap.md index dac49e7..af2e61b 100644 --- a/fixtures/acceptance/stage_0_2_roadmap.md +++ b/fixtures/acceptance/stage_0_2_roadmap.md @@ -192,6 +192,17 @@ `S2-GRAPH-004` `S2-GRAPH-005` `S2-GRAPH-006` +`S2-GRAPH-EDGE-001` +`S2-GRAPH-PROV-001` +`S2-UNIT-EMPTY-001` +`S2-ASSET-MODEL-001` +`S2-ASSET-WEAR-001` +`S2-ASSET-MATERIAL-001` +`S2-ASSET-TEXM-001` +`S2-RUNTIME-DRAFT-001` +`S2-CLI-JSON-001` +`S2-LICENSED-MISSION-P1-001` +`S2-LICENSED-MISSION-P2-001` `S2-PROP-001` `S2-FUZZ-001` `L3-P1-MSH-001` @@ -204,8 +215,8 @@ `L3-P2-WEAR-001` `L3-P1-ASSET-001` `L3-P2-ASSET-001` -`L3-P1-CAPTURE-001` -`L3-P2-CAPTURE-001` +`S2-PLANNING-CAPTURE-P1-001` +`S2-PLANNING-CAPTURE-P2-001` `S3-WEAR-001` `S3-WEAR-002` `S3-WEAR-003` @@ -256,15 +267,23 @@ `S3-MAT-RESOLVE-003` `S3-MAT-RESOLVE-004` `S3-MAT-RESOLVE-005` -`S3-RENDER-001` -`S3-RENDER-002` -`S3-RENDER-003` -`S3-RENDER-004` -`S3-RENDER-005` -`S3-RENDER-006` -`S3-RENDER-007` -`S3-RENDER-008` -`S3-RENDER-009` +`S2-PLANNING-RENDER-001` +`S2-PLANNING-RENDER-002` +`S2-PLANNING-RENDER-003` +`S2-PLANNING-RENDER-004` +`S2-PLANNING-RENDER-005` +`S2-PLANNING-RENDER-006` +`S2-PLANNING-RENDER-007` +`S2-PLANNING-RENDER-008` +`S2-PLANNING-RENDER-009` +`S3-VK-MESH-UPLOAD-001` +`S3-VK-TEXM-UPLOAD-001` +`S3-VK-DESCRIPTOR-001` +`S3-VK-PIPELINE-001` +`S3-VK-DRAW-MODEL-001` +`S3-VK-DRAW-TERRAIN-001` +`S3-VK-PIXEL-CAPTURE-001` +`S3-VK-VALIDATION-001` `S3-GL-001` `S3-GL-002` `S3-GL-003` |
