Blacksite/docs/editor/evaluations/content-workspace-m2/README.md

22 KiB

M2 Content Workspace Native Evaluation

Evidence record — not current product guidance. Use the documentation index for current behavior and architecture.

Date: 2026-07-14

Milestone: M2 - Content workspace and asset pipeline

Epic: Gitea #59

Base commit: 931f561e7ff1489f897e22ebb3cc7545fcfef702

Implementation revision: Candidate tree passed all gates; exact commit assigned at publication

Release-candidate commit: Pending publication commit

Native interaction acceptance: Pass for the workflows recorded below

Pinned Penpot acceptance targets: export index

This feature-level record covers the Content Browser interaction pass added during M2 completion. It does not close the milestone or replace the exact-commit, clean-checkout, import/reimport, forward/Solari, packaging, and independent signoff gates in the M2 plan.

Native Protocol

The normal debug editor was rebuilt with cargo build -p editor --bin editor, launched as LD_LIBRARY_PATH=target/debug/deps target/debug/editor --project ., and exercised in native Wayland windows at 3426x1384 and 1440x3440. The project stayed live throughout each workflow and was stopped without saving the temporary placement scene.

Workflow Result Evidence
Managed storage visibility Pass assets/.index, .trash, .thumbnails, .import-cache, and generated artifact directories were absent from the tree and content grid; ordinary content folders remained visible.
Empty workspace context Pass Right-clicking visible unused content space cleared the selected model and opened Import Here, PBR grouping, Create Material Here, New Folder, Open Trash, Paste, Undo, Select All, Refresh, and view actions.
Multi-selection Pass Ctrl-click selected assets/build_profiles and assets/levels together; both tiles, the two-item footer, and the multi-selection Details summary stayed synchronized.
Batch context menu Pass Right-clicking one selected folder preserved both selections and exposed Cut, Copy, Duplicate, and Move Selection To Trash.
Item context menu Pass Right-clicking the Poly Haven desk model selected it and exposed model actions plus Rename, Cut, Copy, Duplicate, and Move To Trash.
Details resizing Pass Dragging the Content Browser divider expanded the Details pane; its hover affordance described drag resize and double-click reset.
Background/item priority Pass The workspace background menu remained available without intercepting folder/model clicks or Ctrl multi-selection.
Destination-first bundled import Pass Created assets/Props/Office/ through reviewed folder transactions, chose Import Here from genuinely empty space, and imported the Poly Haven metal_office_desk_2k.gltf directly there with its .bin and three relative textures. No type-specific routing occurred.
Import To and final-path review Pass Chose assets/Furniture/Office without navigating away from the root browser view, opened the native picker, selected /tmp/m2-live.png, and confirmed the second review showed the pinned destination plus exact assets/Furniture/Office/m2-live.png target. Cancel closed the review and a filesystem assertion confirmed that no target was published.
Destination-first skeletal import and placement Pass Chose Import Here in assets/Furniture/Office/, imported blacksite-m2-robot-expressive.glb directly there, inspected its stable draw-slot IDs, and placed it as a live SkinnedMesh actor with its skeleton hierarchy intact. Registry ID 5b4640dd-fc17-4703-b9ea-e4eb49a8c7fc and both generated manifests survived the final headless refresh.
Editable material extraction Pass The review exposed the converted metallic/roughness values, base/normal/packed texture channels, double-sided state, destination path, and stable source provenance before one transactional publication.
Re-extraction diff/apply safety Pass after corrective implementation Native review found the provenance-matched Material, exposed a source-revision diff, and kept Extract disabled until Apply Reviewed Update or Create New Copy was chosen. Apply preserved Material ID 647a63d4-64c6-4431-ba46-9efdd515cd8f, mapped all eight active slots, and Undo Content restored the exact prior Material bytes. Pipeline tests additionally prove external-edit rejection and exact-byte rollback when dependent publication fails; editor tests prove undecided and stale reviews cannot publish.
Extract-and-map contract Pass after corrective implementation Native QA found that extraction initially created the Material without changing model defaults. The transaction now registers the Material and maps all nine draw-slot IDs using source material 0 before publishing either registry document; the generated static-mesh manifest contains the same nine Project(...) selections.
Guarded create/write undo Pass Undo Content moved the first extracted Material into one Trash batch while leaving the model bundle untouched. The corrected extraction was then rerun against the same fixture.
Forward/Solari DefaultGrid visibility Pass The scene's UV-independent neutral grid remained visible in Lit / Forward and after applying GiMode::Solari in memory; assets/project.ron remained configured as Forward.
Broken project fallback safety Pass Selected new_material_2 as the project fallback, moved that Material to Trash through the reviewed transaction, and confirmed affected primitives and the placed skinned model remained visible with DefaultGrid. Undo restored the Material; the project fallback was then cleared back to the built-in default.
Skinned scene-slot override Pass The Inspector exposed every imported draw slot and its stable ID. Assigning new_material_2 to the first slot changed only that scene slot to an explicit project Material while the remaining slots continued to report imported-source fallback.
Restart persistence and deterministic refresh Pass After a clean close/restart, the registry, runtime catalog, generated static-mesh manifest, and extracted Material retained identical SHA-256 hashes. Registry and manifest each retained nine project slot selections.
Editor/headless classification parity Pass after corrective implementation process-assets --check found that browser sync had collapsed a schema-classified Material Instance back to Material. Browser registry sync now reclassifies the authored file while retaining the unified visible Material family. A steady-state editor restart preserved both document hashes and the following headless check reported zero changes.
Reimport and orphan reconciliation Pass after corrective implementation Removing source node 8 from the QA glTF and choosing Reimport retained eight active Project(...) mappings by stable slot ID and exposed the removed mapping as one explicit orphan. Locate selected its extracted Material; Clear Orphan plus Apply published eight active mappings and zero orphans to registry and runtime manifests. The source bundle under source_assets/ was not modified.
Move/rename stable identity Pass after corrective implementation A reviewed Cut/Paste moved assets/Props/Office/ to assets/Furniture/Office/ while preserving model ID 36c51ff3-29ae-4ab1-aaca-f90f57e27966 and Material ID 647a63d4-64c6-4431-ba46-9efdd515cd8f. Native QA found stale nested MaterialRef.source_path caches on the first attempt; guarded Undo restored the prior bytes, the transaction rewrite was corrected, and the repeated move updated registry, catalog, both model manifests, Material provenance, and texture paths. Reimport at the destination retained all eight mappings.
Duplicate identity and internal references Pass after corrective implementation Native Duplicate assigned fresh IDs to the copied model, Material, and textures; every copied model slot referenced the copied Material and folder, while the original records remained unchanged. Copied models discarded inherited manifest paths and the watcher regenerated static/animation manifests under the copied model ID.
Duplicate guarded undo and derived cleanup Pass after corrective implementation Native Undo initially exposed orphan generated manifests after removing the copied tree. Trash manifest schema v2 now includes model static/animation artifacts. Rebuilt native Duplicate -> Undo moved the copy plus both generated manifests into one restorable Trash batch, removed copied registry/catalog records, and preserved the original tree and artifacts.

Automated and Packaged Acceptance

The same local implementation passed the complete workspace/all-feature test suite, strict all-target/all-feature Clippy with warnings denied, formatting, both project validators, and a steady-state headless content check over 78 discovered assets. validate-levels audited 152 dependencies with zero blockers; validate-samples audited all five samples and 157 dependencies with zero blockers. Both validators reported the expected eight non-blocking import/platform findings. The final process-assets --check reported zero registry, catalog, model, texture, or material-artifact changes. The QA package published 125 runtime files (87 written and 38 reused) with no stale packaged files.

Focused Content Browser regressions also cover guarded undo for created folders and copied content: an unchanged created result is targeted as one restorable Trash batch, while any external tree change blocks the undo and preserves the workspace. Ordinary Move to Trash records its exact batch for guarded Ctrl/Cmd+Z restoration as well. Model trash/restore additionally verifies byte-exact static/animation manifest removal and restoration, and copy coverage verifies fresh IDs plus internal-only reference remapping.

The final transaction audit adds focused coverage for exact browser/scene status restoration when a content review is cancelled, stale-drag cleanup, registry/runtime-catalog drift refusal during an import review, and reviewed-import rollback that removes newly published bytes without deleting an in-project adopted asset. Destructive reference discovery reports external authored documents, project defaults, model mappings, and dependency records while excluding references removed in the same selected tree. Ambiguous out-of-editor fingerprint moves now leave registry/manifests unchanged and open an explicit identity-repair review in the editor; the user can preserve one candidate ID or intentionally register the file as new. Headless processing reports both candidate paths until that choice is published instead of silently allocating a replacement ID. Focused repair coverage proves preserved-ID continuity through the next shared scan, intentional new-ID behavior, duplicate-ID rejection, and one commit that publishes the world registry, registry file, and stripped runtime catalog together. Content shortcuts are also suppressed while a browser review or popup owns input, and Cut/Paste keeps the requested name so collisions reach review. The follow-up QoL audit made right-click targeting consistent for files, folders, and embedded subassets; distinguishes nested selections covered by a selected parent from genuinely unaffected built-ins/subassets; and clears selection when search/scope/type filters would hide it. Browser controls are disabled behind reviews. Startup, manual Refresh, and watcher processing now share the same pipeline, rediscover unresolved move repairs after restart, and selection-only changes no longer trigger whole-catalog fingerprint or registry work. A rebuilt native editor then mapped successfully on the live Wayland session with the shared startup refresh active. The Details drag strip now uses one stable explicit egui ID across layout passes; a final bounded launch produced no resize-handle ID warning, and the following headless check again reported zero content changes.

The architecture audit also moved destination-first import planning/publication, source fingerprints, static-mesh normalization, animation normalization, and combined model-artifact publication into content_pipeline. The process-assets feature's dependency tree no longer contains the editor crate; the editor retains compatibility re-exports for its UI consumers.

The extraction correction adds focused coverage for dependent registry edits that can see newly registered asset IDs, byte-restoring rollback when that dependent edit fails, and source-index mapping across every draw slot that uses an extracted source material. Re-extraction coverage also requires an explicit decision for provenance matches, rejects stale external edits, preserves the existing asset ID on Apply, and restores exact prior bytes if dependent publication fails.

Property-block coverage verifies owner/slot-local Standard handles, stable handle counts across repeated application, strongest precedence above an existing Material Instance on the Surface path, and whole-block rejection for invalid Standard/schema/type/range/texture input. Invalid blocks leave the resolved base handle visible and emit one deduplicated diagnostic.

The reviewed promotion transaction has focused coverage for a direct Material and an existing Material Instance base, exact-slot assignment, one scene-history edit, cancel without mutation, source/slot/schema/block/target conflicts after review, invalid runtime input, and restoration of the exact prior file, registry, and runtime-catalog bytes when the scene edit fails. The import review likewise has focused coverage for non-mutating planning, collision refusal, post-review source/registry/runtime-catalog changes, complete textual-glTF dependency fingerprints, artifact processing failure with whole-bundle rollback, adopted-file preservation, and an explicitly pinned Import To destination that does not change the visible browser location.

The final native promotion scenario used the editor-only BRP reflection boundary to inject a two-parameter MaterialPropertyBlock into Ground's exact slot:primitive:surface. The Inspector reviewed and published one sparse direct-base Material Instance, assigned it to only that primitive slot through one history edit, and removed the runtime block only after publication succeeded. BRP readback proved the exact Material Instance reference and absence of the runtime component; Undo restored the original Pebble Bricks slot. The temporary instance, registry, catalog, scene, and Pebble fixture were then restored to their pre-scenario bytes. Runtime blocks remain absent from Add Component and scene/prefab persistence; no QA-only authoring component was added.

The final moved/reimported QA fixture hashes were:

  • registry: e1eb425c2d52be06d7db840df1f14f1d3a96daf2c1876dfc025d861bf0d32b74
  • runtime catalog: cc296049b656d52e3159cca2e11f52bc4bdef0c922fb0dc00fa5d33f4e3748e7
  • desk static-mesh manifest: 6c69a9f9502fce36cb88e82a7e2e2074e6279ca6da4d49e3e36c1ede35c0577f
  • desk animation manifest: 8a0632bd8b6ea4a5d117214ea168f4020f9399c92bb9da3d7cba1ad371b9e4e8
  • extracted desk Material: 70e498f596d37da5735f5e0d0b1f3834442cd1aefa698232a640fec9d9e422f3
  • skeletal static-mesh manifest: ee9168b123d89ff57b312a35e54a3a8d5bf5b9dae3ed98e79c7bc24dcb6d6454
  • skeletal animation manifest: 7c14b6ad016db5c09af7f56f50492a05f9bdd89b1bb672e9886bc9a3447e2130

The latest 2026-07-14 rerun of cargo package-project --profile development wrote zero changed files and reused 74 unchanged files. The packaged assets/content.catalog.ron was byte-identical to the source runtime catalog with SHA-256 cc296049b656d52e3159cca2e11f52bc4bdef0c922fb0dc00fa5d33f4e3748e7. A bounded native package launch hydrated the authored scene and visibly rendered the world-space DefaultGrid across UV-less primitives and the floor. The corrective binary also mapped a live 3426x1384 Hyprland window as class bevy-fps-foundation, title Bevy FPS Foundation, before the bounded QA process stopped.

Screenshot Captures

The native PNGs are retained locally pending upload as Gitea issue attachments. Repository image copies are intentionally not required by the evaluation policy.

Capture Dimensions SHA-256
blacksite-m2-managed-hidden.png 3426x1384 RGB PNG 97bb710d569039b54218a556865f366cff3bda2ae6ce3afe4a1933db8e58c36b
blacksite-m2-empty-clears-selection.png 3426x1384 RGB PNG e162c865202717cf36eec7a37c3f95a4cc4c3c55dcc5d48243b00fe47be0d136
blacksite-m2-multiselect-fixed-2.png 3426x1384 RGB PNG 729ae5d2b10ca2d8acdd249f19c35f5e6f3a57d9dbb9550f80c371b703336868
blacksite-m2-multiselect-context.png 3426x1384 RGB PNG 88232360d592a1e4436e86a17fe1356ea10d7766cfbdb98eb6323767cbc16fe6
blacksite-m2-item-context.png 3426x1384 RGB PNG 6f01f23ea7ad65dffecd0d0e51e57b6a1fcf26cf06cfe6f536f8376557a6649b
blacksite-m2-details-resized.png 3426x1384 RGB PNG 407ee7779367f83ca2a9c5d6f4a5803584609e319d881265f9544b194fbd0296
blacksite-m2-office-empty-context.png 3426x1384 RGB PNG a4004292f70b8a07f6c0d24e8bc47c1716e7de33b4b627a67c4198afe76a00a3
blacksite-m2-imported-model-details.png 3426x1384 RGB PNG cf68b41293b823945fdd17e9752d3733f8d38144504f5fdbfd9be7f793ce0205
blacksite-m2-extraction-review.png 3426x1384 RGB PNG f4d567d3353536ca1492d48fe59f72ff5b3e4b37c095ef27eaed51347c6a67ec
blacksite-m2-extraction-transaction.png 3426x1384 RGB PNG 99944392580a88ec14868dbf5a422bacc89ca3b8d8d0750311f429e65250aef6
blacksite-m2-details-wide-mapped.png 3426x1384 RGB PNG 6aee075b4006909cde1625d5afd42a8be93b9323de93156bd8a55d27ef07e678
blacksite-m2-defaultgrid-solari.png 3426x1384 RGB PNG 9f23685f6354029ce772878a3f080c7efb045ccc73fce6c19f8fe7802e378ee8
blacksite-m2-orphan-controls.png 3426x1384 RGB PNG afe8f07d92c00ac1294b021682a66ea623b3c22158598119d2617f2e0b1dae72
blacksite-m2-orphan-locate.png 3426x1384 RGB PNG d7c841747e1a712da80537846699baa8b2f83f91a65ddc298d1d3e665f2e74b7
blacksite-m2-orphan-applied.png 3426x1384 RGB PNG c979981fba5b30692c3f858fd349b582bb204b5720702bd684f3721ed5a76350
blacksite-m2-root-empty-context.png 3426x1384 RGB PNG 960231dc8661f7b012abf7ddf0cc97ce42548e789e911895e12b8b800b29153d
blacksite-m2-office-move-review-fixed.png 3426x1384 RGB PNG aaa06af27049858f2c691b9c251197a19a5330919048b59b2c313d06d2bf4471
blacksite-m2-office-moved-fixed.png 3426x1384 RGB PNG 0cfdd6206d56355583c55e483959c8c154223acf5142015f9f2329da54baf5ca
blacksite-m2-moved-model-reimported.png 3426x1384 RGB PNG a6420434c25f72af94f1f4bdb22c8686bd840aad5fe114c192bdefc6db020aec
blacksite-m2-duplicate-review.png 3426x1384 RGB PNG f0fae051ca8c807540779de662a5381b3e9f1ac3faaf1cd5412bb7f1cb2835b0
blacksite-m2-duplicate-committed.png 3426x1384 RGB PNG 3e3b0fd38f0374c47598307d93f5238028d269753742f4b296536f0fe886c1a5
blacksite-m2-duplicate-undo-clean-editor.png 3426x1384 RGB PNG 45aaa55e2069de66d9eb86f4157fd143d92afa2de07aaefb0bb2cc960958401a
blacksite-m2-packaged-live-window.png 3426x1384 RGB PNG de8c19b2dbc2b488e290b5e4d1bd3e1c279388374a81e25a5684dd2454f58ee2
blacksite-m2-reextract-modal.png 1440x3440 RGB PNG b7139a3b6da88755be1c53ef9921450449f207a3082248efd8199bd8abdea515
blacksite-m2-reextract-selected.png 1440x3440 RGB PNG bd22912ff3d2e4bfdab6469909344a0b0e94f8309665ad876e98e43fe7c80563
blacksite-m2-reextract-applied.png 1440x3440 RGB PNG 007164ae995c64f048aac638e20664fc173769945691afc3e6a0a6fbf4704380
blacksite-m2-reextract-undo.png 1440x3440 RGB PNG 3f95eec702d4df6a55d88132c82282311d96d3bca15e2a8ebe11d06ee408461b
m2-skeletal-import-review.png 1440x3440 RGB PNG a08dc1d17848d3089d601c8b31c697c5f22b36dda625baf5fa8f52ee7dad2ea2
m2-skeletal-selected.png 1440x3440 RGB PNG 2867517356b63a1b7627d5d069e7b45150325065ef247ff20a06c00b53c79581
m2-skeletal-placed.png 1440x3440 RGB PNG 1936dfcd84d9a6e6cf8f293f71d8a4cb89d1761ad9ee982954ad25682cfd999d
m2-project-default-selected.png 1440x3440 RGB PNG ee3d5385ccd7af1d099a042fb02400e1e193374ca475a28b3d7f55d483d64547
m2-project-default-broken-fallback.png 1440x3440 RGB PNG 5efb715a619456afcbee808d13f7b4c2cf24bb975f6b69671401bba1fed07ba0
m2-inspector-open.png 1440x3440 RGB PNG 7ae9d456d0924ab50012ecbefb8e42847ccb141793ce89487c0fd2fbd2b8307e
m2-skeletal-scene-override.png 1440x3440 RGB PNG 3c7ec6fce6a09f9e46402aa64dda77c5508703a11149b30297cee80d9e7907af
m2-import-to-selected.png 1440x3440 RGB PNG d9cb38de6fca185dab6887324163698b2ac036e8f5eb19f16fb65415172f9605
m2-import-review.png 1440x3440 RGB PNG 631e1ac1d3c63d72d7c930c3b4789584e15a178f03425fd972df82da51dcfa05
property-block-promotion-20260717T130300Z.png 3426x1384 RGB PNG 69cd668618206b1cee25142521127882cc785031e8e7cd21684c1eab7c97befa

Remaining Release Use

The complete deterministic candidate gate passes on the local candidate tree. After assigning its exact commit, confirm the clean-tree evidence digest, publish the native/candidate evidence on Gitea, and finish with issue/milestone readback. The local candidate tree covers static and skeletal import, edit/reimport/orphan resolution, broken project fallback, scene-slot precedence, reviewed Import To cancellation, runtime-injected property-block promotion, responsive Penpot material UI, dirty/save/restart behavior, and packaged runtime behavior. Publication readback—not another destructive native content-authoring repetition—is the remaining release use of this record.