prometeu-studio/discussion/lessons/DSC-0029-studio-frame-composer-syscall-and-sprite-alignment/LSN-0041-composer-must-own-public-sprite-composition.md
bQUARKz 908cb2b1fe
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 60.68% (15277/25176) * Branch Coverage: 53.63% (5781/10779) * Lines of Code: 25176 * Cyclomatic Complexity: 9960 #### Quality Gates Summary Output truncated.
Test / Build skipped: 11, passed: 547
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
Intrepid/Prometeu/Studio/pipeline/pr-master This commit looks good
housekeep
2026-04-18 17:27:22 +01:00

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
studio
compiler
pbs
stdlib
runtime-alignment
abi
syscall
frame-composer
sprites

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:gfx coverage 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_sprite internally to composer.emit_sprite would 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:gfx example 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:composer wave 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.