--- id: LSN-0032 ticket: frame-composer-public-syscall-surface title: Public ABI Must Follow the Canonical Service Boundary created: 2026-04-18 tags: [gfx, runtime, syscall, abi, frame-composer, scene, camera, sprites] --- ## Context `DSC-0026` finished the internal migration to `FrameComposer` as the canonical frame-orchestration owner, but the public VM-facing ABI still exposed part of that behavior through legacy `gfx`-domain calls. That left the codebase with a mismatch between the real runtime ownership model and the surface visible to cartridges, tooling, and syscall declarations. `DSC-0027` closed that gap by introducing the `composer.*` public domain and removing the old public sprite path. ## Key Decisions ### Public Syscalls Must Expose the Real Owner **What:** Scene binding, camera control, and sprite emission now live under `composer.*`, and the legacy public `gfx.set_sprite` path is gone. **Why:** Once `FrameComposer` became the canonical orchestration service, keeping public orchestration under `gfx.*` would preserve the wrong mental model and encourage callers to treat the render backend as the owner of frame policy. **Trade-offs:** This forces migration across ABI declarations, runtime dispatch, tests, and tooling, but it removes the long-term cost of a misleading public boundary. ### Domain-Specific Status Types Preserve Architectural Meaning **What:** Mutating public composer operations return `ComposerOpStatus` instead of reusing backend-oriented status naming. **Why:** Operational outcomes for scene binding or sprite emission are not backend-domain results. Reusing `GfxOpStatus` would blur the boundary that the migration was trying to make explicit. **Trade-offs:** This adds one more status family to maintain, but it keeps the public ABI semantically aligned with the actual service contract. ## Patterns and Algorithms - Promote internal ownership changes into the public ABI as part of the same migration thread. - Use syscall domains to encode service boundaries, not just namespace aesthetics. - Remove obsolete public fallbacks completely when they preserve the wrong operational model. - Keep runtime dispatch, bytecode declarations, and tooling aligned so the public path is exercised end to end. ## Pitfalls - Leaving a legacy public syscall alive after the internal model changes creates a dual-contract system that is harder to remove later. - Migrating runtime dispatch without migrating declarations and tooling can leave hidden ABI drift in tests and generators. - Reusing backend-specific status names in the wrong domain quietly leaks old ownership assumptions into new APIs. ## Takeaways - The public ABI should mirror the canonical service boundary, not historical implementation leftovers. - Namespace changes are architectural when they change who is responsible for a behavior. - Removing a legacy public entrypoint is often safer than preserving a compatibility shim that encodes the wrong model.