# Bank Composition Editor ## Original Problem `Bank Composition` needed to become a real editor inside `Asset Details` without collapsing into any of these bad outcomes: - direct binding to raw snapshot or manifest shapes; - overgeneric UI components before the first section existed; - family-specific rules hidden inside visual controls; - direct Studio writes to `asset.json`; - public bus chatter for every local section interaction. ## Consolidated Decision The first-wave `Bank Composition` model is: 1. Studio owns a DTO projection boundary for the section; 2. the section shell lands early and follows the existing staged-edit rhythm; 3. dual-list and capacity-meter components stay intentionally dumb; 4. one section-scoped coordinator around `StudioFormSession` owns orchestration; 5. public workspace-bus events stay minimal and notification-oriented; 6. `apply` goes through packer and rebinds from refreshed persisted state. ## Final Model ### 1. DTO Boundary The section never consumes raw packer/runtime structures directly. It works with Studio-owned DTOs for: - `available` rows from current runtime/details state; - `selected` rows from persisted composition state. Only non-blocking files enter the `available` DTO projection. ### 2. Section Shell `Bank Composition` is a real `Asset Details` section, not a hidden future placeholder. The shell follows the existing staged-edit rhythm: - `change` - `apply` - `reset` - `cancel` Outside edit mode, it teaches the current persisted state in read-only form. ### 3. Dumb Components Two named components exist for this area: - `StudioDualListView` - `StudioAssetCapacityMeter` They are intentionally state-driven, not rule-driven. Family-specific transfer, ordering, and capacity semantics do not live inside these components. ### 4. Section-Scoped Coordinator The main orchestration lives in one section-scoped coordinator built around `StudioFormSession`. That coordinator owns: - draft lifecycle; - transfer and reorder effects; - capacity state; - blockers and hints; - apply success and failure handling. ### 5. WorkspaceBus Boundary The public bus surface stays intentionally small. First-wave public notifications are about observable state transitions: - draft changed; - capacity changed; - apply requested; - apply succeeded; - apply failed. Local edit actions remain local to the section coordinator. ### 6. Persistence Boundary Studio does not write `asset.json` directly for `Bank Composition`. The write path is: 1. Studio submits ordered selected files to packer; 2. packer validates and persists through `asset.json.artifacts`; 3. packer refreshes the minimum runtime/details state; 4. Studio rebinds from refreshed persisted state. If apply fails, the draft stays alive and the section remains in editing mode. ## Examples ### Example: Why the meter must stay dumb Tile banks and sound banks may calculate capacity differently. That difference belongs in family policies owned by the coordinator, not in `StudioAssetCapacityMeter`. ### Example: Why `apply` is not a public bus command External observers may care that apply was requested or failed. They do not need to own the local section command sequence for `moveUp`, `moveDown`, `reset`, or `cancel`. ## Common Pitfalls and Anti-Patterns - Binding section UI directly to raw snapshot rows or raw manifest shape. - Hiding bank-family rules inside `StudioDualListView` or the capacity meter. - Creating a second staged-edit session model parallel to `StudioFormSession`. - Turning every local section action into a public workspace-bus event. - Writing `asset.json` directly from Studio instead of routing through packer. ## References - Specs: - [`../specs/4. Assets Workspace Specification.md`](../specs/4.%20Assets%20Workspace%20Specification.md) - Cross-domain: - [`../../packer/specs/5. Diagnostics, Operations, and Studio Integration Specification.md`](../../packer/specs/5.%20Diagnostics,%20Operations,%20and%20Studio%20Integration%20Specification.md) - Related learn: - [`./assets-workspace-execution-wave.md`](./assets-workspace-execution-wave.md)