--- id: DEC-0015 ticket: frame-composer-public-syscall-surface title: FrameComposer Public Syscall Surface status: accepted created: 2026-04-17 accepted: 2026-04-17 agenda: AGD-0027 plans: [PLN-0022, PLN-0023, PLN-0024, PLN-0025] tags: [gfx, runtime, syscall, abi, frame-composer, scene, camera, sprites] --- ## Status Accepted. ## Contexto `DEC-0014` locked `FrameComposer` as the canonical internal frame orchestration service and `PLN-0017` through `PLN-0021` completed that internal migration path. `Hardware` now owns `FrameComposer`, the runtime renders through `FrameComposer.render_frame()`, and scene/camera/cache/resolver/sprite ownership no longer belongs canonically to `Gfx`. That migration did not define the equivalent public syscall contract for VM code. The public ABI still exposed legacy `gfx`-domain sprite control while the canonical scene/camera operations existed only as internal driver APIs. This decision closes that public ABI gap without reopening the already accepted internal ownership model. ## Decisao The canonical public syscall surface for frame orchestration SHALL move to the `composer.*` namespace. Normatively: - The canonical public service domain for `FrameComposer` operations SHALL be `composer`. - The initial canonical syscall set SHALL be: - `composer.bind_scene(bank_id) -> ComposerOpStatus` - `composer.unbind_scene() -> ComposerOpStatus` - `composer.set_camera(x, y)` - `composer.emit_sprite(glyph_id, palette_id, x, y, layer, bank_id, flip_x, flip_y, priority) -> ComposerOpStatus` - `composer.emit_sprite(...)` SHALL be the canonical public sprite submission path. - `composer.emit_sprite(...)` MUST NOT require a caller-provided sprite index. - `composer.emit_sprite(...)` MUST carry `layer` and `priority`. - `composer.emit_sprite(...)` MUST NOT expose `active` as part of the canonical contract. - `composer.bind_scene(...)`, `composer.unbind_scene()`, and `composer.emit_sprite(...)` SHALL return `ComposerOpStatus`. - `composer.set_camera(x, y)` SHALL keep the minimal V1 camera contract already accepted by `DEC-0014`: - `x` and `y` are `i32` pixel coordinates; - they represent the top-left viewport origin in world space. - The public ABI MUST NOT expose cache refresh policy or explicit refresh controls. - The public ABI MUST NOT expose scene/camera introspection in this first phase. - `gfx.set_sprite(...)` MUST be removed completely from the public contract. - No compatibility shim for `gfx.set_sprite(...)` SHALL remain as part of the canonical migration target. - Introduction of `composer.*` and removal of `gfx.set_sprite(...)` SHALL be executed in the same migration thread. ## Rationale The public ABI must reflect the accepted ownership model rather than preserve a misleading legacy shape. Keeping the canonical public surface under `gfx.*` would continue to tie orchestration semantics to the wrong service boundary. The new namespace makes the ownership change explicit: - `Gfx` is the visual backend; - `FrameComposer` is the frame orchestration service. Removing `gfx.set_sprite(...)` completely avoids prolonging a dual public sprite model. A compatibility shim would preserve legacy slot/index semantics in the public contract after those semantics had already ceased to be canonical internally. Returning `ComposerOpStatus` for operational mutating calls preserves status-first behavior while keeping the public contract aligned with the new service boundary. Reusing `GfxOpStatus` would leak backend-domain semantics into orchestration-domain syscalls after that separation had already been made explicit. Deferring introspection and explicit refresh controls keeps the first public ABI focused on control, not diagnostics or internal policy leakage. ## Invariantes / Contrato ### 1. Namespace - Public frame-orchestration syscalls MUST live under `composer.*`. - `composer.*` SHALL be treated as the canonical public orchestration surface. - `gfx.*` SHALL NOT remain the canonical public orchestration namespace for scene/camera/sprite submission. ### 2. Scene Control - `composer.bind_scene(bank_id)` MUST bind by scene bank id. - Binding semantics MUST remain aligned with `DEC-0014`: - scene resolution through the scene bank pool; - explicit bind/unbind lifecycle; - no implicit per-frame rebinding. - `composer.unbind_scene()` MUST leave no-scene rendering valid. - `ComposerOpStatus` SHALL be the canonical operational status family for composer-domain mutating syscalls. ### 3. Camera - `composer.set_camera(x, y)` MUST remain the minimal V1 camera API. - Camera follow, smoothing, shake, transitions, and readback are OUT OF SCOPE for this decision. ### 4. Sprite Submission - `composer.emit_sprite(...)` MUST be frame-emission based. - The caller MUST NOT provide sprite slot/index information. - The public payload MUST include: - `glyph_id` - `palette_id` - `x` - `y` - `layer` - `bank_id` - `flip_x` - `flip_y` - `priority` - The canonical public sprite contract MUST NOT include `active`. - Overflow behavior SHALL remain aligned with `DEC-0014`: - excess sprites are ignored; - overflow is not a hard VM fault in V1. ### 5. Non-Goals for V1 Public ABI - No public refresh/invalidate syscalls. - No public cache inspection syscalls. - No public `scene_status()` syscall. - No public `get_camera()` syscall. ### 6. Migration Contract - Migration MUST update: - syscall registry and ABI resolution; - runtime dispatch; - bytecode/cartridge declarations; - tests; - stress cartridges and related tooling where applicable. - Migration MUST NOT leave `gfx.set_sprite(...)` as a supported public fallback after the new contract lands. ## Impactos ### HAL - The syscall enum, registry, metadata, and resolver will need a new `composer` domain surface. - `gfx.set_sprite(...)` must be removed from the public ABI contract. - A new `ComposerOpStatus` contract will need to be introduced for composer-domain operational returns. ### Runtime / VM - Runtime dispatch must route public scene/camera/sprite orchestration through `FrameComposer`. - Existing bytecode declarations and cartridges that rely on `gfx.set_sprite(...)` will need coordinated migration. ### Spec / ABI / ISA_CORE - The canonical spec for the public VM-facing graphics/composition surface must be updated to reflect `composer.*`. - ABI-facing documentation and contracts must be updated wherever syscall domain, names, arguments, or return semantics are specified. - `ISA_CORE` must be updated if and where it normatively references the public syscall surface affected by this decision. ### Drivers / Hardware - `FrameComposer` already has the required internal base; execution work will focus on public ABI exposure rather than internal ownership redesign. ### Tooling / Stress - Stress cartridges and bytecode generators can only exercise the canonical frame path publicly after `composer.*` exists. ## Referencias - [AGD-0027-frame-composer-public-syscall-surface.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/agendas/AGD-0027-frame-composer-public-syscall-surface.md) - [DEC-0014-frame-composer-render-integration.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/decisions/DEC-0014-frame-composer-render-integration.md) ## Propagacao Necessaria - A new implementation plan MUST be created from this decision before code changes. - The plan MUST cover ABI introduction, legacy syscall removal, cartridge/test migration, regression coverage, and canonical spec propagation. - The plan MUST explicitly assess and update ABI and `ISA_CORE` artifacts where this decision changes documented public behavior. - Stress tooling SHOULD be updated as part of the migration thread so the public ABI can exercise the canonical frame path end-to-end. ## Revision Log - 2026-04-17: Initial accepted decision from `AGD-0027`.