4.4 KiB
| id | ticket | title | created | tags | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| LSN-0041 | studio-frame-composer-syscall-and-sprite-alignment | Composer Must Own the Public Sprite-Composition Surface | 2026-04-18 |
|
Context
The runtime had already moved canonical frame orchestration to FrameComposer and published the corresponding public ABI under composer.*, but Studio still exposed sprite composition through @sdk:gfx and Gfx.set_sprite.
That left the repository teaching the wrong owner even though runtime dispatch, architectural lessons, and the public syscall domain had already changed. The migration closed that drift for the sprite path without pulling scene binding, camera control, or scene-bank work into the same ticket.
Key Decisions
Public Source Ownership Must Match the Runtime ABI
What:
Studio now exposes sprite composition through @sdk:composer, with LowComposer as the low-level host owner and Composer as the public service facade. The old Gfx.set_sprite path was removed instead of being kept as compatibility sugar.
Why:
Once runtime ownership moved to FrameComposer, keeping sprite submission under gfx.* would preserve a false mental model and create a dual contract between what the runtime actually owns and what PBS/stdlib teaches.
Trade-offs: The migration required coordinated changes across stdlib resources, specs, frontend conformance, examples, and LSP fixtures. That was more work up front, but it avoided a longer-lived compatibility layer that would have encoded the wrong architecture.
A Partial Domain Rollout Is Valid If the Boundary Is Explicit
What:
This wave shipped only composer.emit_sprite(...) in Studio. bind_scene, unbind_scene, and set_camera were explicitly deferred.
Why: The current pipeline only needed sprite composition. Pulling scene/camera rollout into the same thread would have mixed ABI convergence with unfinished scene-work concerns and made the migration harder to execute cleanly.
Trade-offs:
The docs had to state clearly that @sdk:composer exists now for sprite emission while other composer members remain future work. That is slightly less symmetrical in the short term, but it keeps the implemented contract honest.
Raw Status Returns Can Be Preserved During Ownership Migration
What:
Composer.emit_sprite(...) kept the raw int status return for this wave instead of introducing a new editorial status shell.
Why: The architectural problem in this discussion was ownership and ABI drift, not status typing. Keeping the return shape stable allowed the migration to focus on the service boundary and public surface without coupling it to a second API redesign.
Trade-offs: The API remains less expressive than a nominal status shell, but the migration stayed narrow and easier to validate end to end.
Patterns and Algorithms
- Treat runtime service ownership changes as public-source changes, not merely internal lowering rewrites.
- When the runtime removes a legacy public syscall path, prefer removing the matching source-level facade instead of silently retargeting it.
- Split migrations by boundary: first stdlib/spec surface, then compiler/conformance, then examples and fixture cleanup.
- Keep primitive-oriented
@sdk:gfxcoverage intact while moving only frame-composition behavior to@sdk:composer. - Use a negative regression test for the removed API so compatibility leakage is caught explicitly.
Pitfalls
- Retargeting
Gfx.set_spriteinternally tocomposer.emit_spritewould have looked convenient, but it would have preserved the wrong owner in the public teaching surface. - A repository sweep that ignores examples and fixtures leaves stale guidance behind even when compiler tests are green.
- Replacing every
@sdk:gfxexample wholesale would be overcorrection; primitive and overlay operations still belong there. - Updating docs to mention deferred composer members as if they already shipped would create a second kind of contract drift.
Takeaways
- Public SDK ownership should mirror canonical runtime ownership, not historical implementation leftovers.
- Removing the old API is safer than keeping a compatibility facade that encodes the wrong subsystem boundary.
- A sprite-only
@sdk:composerwave is acceptable when the deferred scene/camera scope is stated explicitly. - Ownership migration and status-typing redesign do not need to happen in the same change.