From 73cf96ed6c62635ffca24863dd3e83606834d468 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sat, 18 Apr 2026 16:31:23 +0100 Subject: [PATCH] housekeep --- .../.backups/index.ndjson.20260418-162736.bak | 29 ++ .../.backups/index.ndjson.20260418-162925.bak | 29 ++ discussion/index.ndjson | 8 +- ...sition-belongs-above-the-render-backend.md | 74 ++++ ...t-follow-the-canonical-service-boundary.md | 56 +++ ...al-overlay-not-part-of-game-composition.md | 56 +++ ...-all-scene-cache-and-camera-integration.md | 405 ------------------ ...7-frame-composer-public-syscall-surface.md | 214 --------- ...erred-overlay-and-primitive-composition.md | 140 ------ ...-0014-frame-composer-render-integration.md | 205 --------- ...5-frame-composer-public-syscall-surface.md | 166 ------- ...rred-gfx-overlay-outside-frame-composer.md | 150 ------- ...me-composer-core-and-hardware-ownership.md | 121 ------ ...ite-controller-and-frame-emission-model.md | 127 ------ ...9-scene-binding-camera-and-scene-status.md | 135 ------ ...020-cache-refresh-and-render-frame-path.md | 136 ------ ...ement-callsite-migration-and-regression.md | 123 ------ ...ser-syscall-domain-and-spec-propagation.md | 122 ------ ...ser-runtime-dispatch-and-legacy-removal.md | 112 ----- ...tridge-tooling-and-regression-migration.md | 107 ----- ...PLN-0025-final-ci-validation-and-polish.md | 96 ----- ...x-overlay-contract-and-spec-propagation.md | 93 ---- ...PLN-0027-deferred-gfx-overlay-subsystem.md | 104 ----- ...rame-end-overlay-integration-and-parity.md | 106 ----- ...-final-overlay-ci-validation-and-polish.md | 82 ---- 25 files changed, 248 insertions(+), 2748 deletions(-) create mode 100644 discussion/.backups/index.ndjson.20260418-162736.bak create mode 100644 discussion/.backups/index.ndjson.20260418-162925.bak create mode 100644 discussion/lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md create mode 100644 discussion/lessons/DSC-0027-frame-composer-public-syscall-surface/LSN-0032-public-abi-must-follow-the-canonical-service-boundary.md create mode 100644 discussion/lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md delete mode 100644 discussion/workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md delete mode 100644 discussion/workflow/agendas/AGD-0027-frame-composer-public-syscall-surface.md delete mode 100644 discussion/workflow/agendas/AGD-0028-deferred-overlay-and-primitive-composition.md delete mode 100644 discussion/workflow/decisions/DEC-0014-frame-composer-render-integration.md delete mode 100644 discussion/workflow/decisions/DEC-0015-frame-composer-public-syscall-surface.md delete mode 100644 discussion/workflow/decisions/DEC-0016-deferred-gfx-overlay-outside-frame-composer.md delete mode 100644 discussion/workflow/plans/PLN-0017-frame-composer-core-and-hardware-ownership.md delete mode 100644 discussion/workflow/plans/PLN-0018-sprite-controller-and-frame-emission-model.md delete mode 100644 discussion/workflow/plans/PLN-0019-scene-binding-camera-and-scene-status.md delete mode 100644 discussion/workflow/plans/PLN-0020-cache-refresh-and-render-frame-path.md delete mode 100644 discussion/workflow/plans/PLN-0021-service-retirement-callsite-migration-and-regression.md delete mode 100644 discussion/workflow/plans/PLN-0022-composer-syscall-domain-and-spec-propagation.md delete mode 100644 discussion/workflow/plans/PLN-0023-composer-runtime-dispatch-and-legacy-removal.md delete mode 100644 discussion/workflow/plans/PLN-0024-composer-cartridge-tooling-and-regression-migration.md delete mode 100644 discussion/workflow/plans/PLN-0025-final-ci-validation-and-polish.md delete mode 100644 discussion/workflow/plans/PLN-0026-gfx-overlay-contract-and-spec-propagation.md delete mode 100644 discussion/workflow/plans/PLN-0027-deferred-gfx-overlay-subsystem.md delete mode 100644 discussion/workflow/plans/PLN-0028-runtime-frame-end-overlay-integration-and-parity.md delete mode 100644 discussion/workflow/plans/PLN-0029-final-overlay-ci-validation-and-polish.md diff --git a/discussion/.backups/index.ndjson.20260418-162736.bak b/discussion/.backups/index.ndjson.20260418-162736.bak new file mode 100644 index 00000000..68838273 --- /dev/null +++ b/discussion/.backups/index.ndjson.20260418-162736.bak @@ -0,0 +1,29 @@ +{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":17,"PLN":30,"LSN":31,"CLSN":1}} +{"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"workflow/agendas/AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"workflow/decisions/DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"workflow/plans/PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} +{"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} +{"type":"discussion","id":"DSC-0022","status":"done","ticket":"tile-bank-vs-glyph-bank-domain-naming","title":"Glyph Bank Domain Naming Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["gfx","runtime","naming","domain-model"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0025","file":"lessons/DSC-0022-glyph-bank-domain-naming/LSN-0025-rename-artifact-by-meaning-not-by-token.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} +{"type":"discussion","id":"DSC-0002","status":"open","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0001","file":"workflow/agendas/AGD-0001-runtime-edge-test-plan.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"workflow/agendas/AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0004","status":"open","ticket":"system-run-cart","title":"Agenda - System Run Cart","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0003","file":"workflow/agendas/AGD-0003-system-run-cart.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0005","status":"open","ticket":"system-fault-semantics-and-control-surface","title":"Agenda - System Fault Semantics and Control Surface","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0004","file":"workflow/agendas/AGD-0004-system-fault-semantics-and-control-surface.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0006","status":"open","ticket":"vm-owned-random-service","title":"Agenda - VM-Owned Random Service","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0005","file":"workflow/agendas/AGD-0005-vm-owned-random-service.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0007","status":"open","ticket":"app-home-filesystem-surface-and-semantics","title":"Agenda - App Home Filesystem Surface and Semantics","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0006","file":"workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0008","status":"done","ticket":"perf-runtime-telemetry-hot-path","title":"Agenda - [PERF] Runtime Telemetry Hot Path","created_at":"2026-03-27","updated_at":"2026-04-10","tags":[],"agendas":[{"id":"AGD-0007","file":"workflow/agendas/AGD-0007-perf-runtime-telemetry-hot-path.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0005","file":"workflow/decisions/DEC-0005-perf-push-based-telemetry-model.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0005","file":"workflow/plans/PLN-0005-perf-push-based-telemetry-implementation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0026","file":"lessons/DSC-0008-perf-runtime-telemetry-hot-path/LSN-0026-push-based-telemetry-model.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0009","status":"open","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0008","file":"workflow/agendas/AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0010","status":"open","ticket":"perf-host-desktop-frame-pacing-and-presentation","title":"Agenda - [PERF] Host Desktop Frame Pacing and Presentation","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0009","file":"workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0011","status":"open","ticket":"perf-gfx-render-pipeline-and-dirty-regions","title":"Agenda - [PERF] GFX Render Pipeline and Dirty Regions","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0010","file":"workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0012","status":"open","ticket":"perf-runtime-introspection-syscalls","title":"Agenda - [PERF] Runtime Introspection Syscalls","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0011","file":"workflow/agendas/AGD-0011-perf-runtime-introspection-syscalls.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0013","status":"done","ticket":"perf-host-debug-overlay-isolation","title":"Agenda - [PERF] Host Debug Overlay Isolation","created_at":"2026-03-27","updated_at":"2026-04-10","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0027","file":"lessons/DSC-0013-perf-host-debug-overlay-isolation/LSN-0027-host-debug-overlay-isolation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0024","status":"done","ticket":"generic-memory-bank-slot-contract","title":"Agenda - Generic Memory Bank Slot Contract","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["runtime","asset","memory-bank","slots","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0029","file":"lessons/DSC-0024-generic-memory-bank-slot-contract/LSN-0029-slot-first-bank-telemetry-belongs-in-asset-manager.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0025","status":"done","ticket":"scene-bank-and-viewport-cache-refactor","title":"Scene Bank and Viewport Cache Refactor","created_at":"2026-04-11","updated_at":"2026-04-14","tags":["gfx","tilemap","runtime","render"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0030","file":"lessons/DSC-0025-scene-bank-and-viewport-cache-refactor/LSN-0030-canonical-scene-cache-and-resolver-split.md","status":"done","created_at":"2026-04-14","updated_at":"2026-04-14"}]} +{"type":"discussion","id":"DSC-0026","status":"open","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-15","tags":["gfx","runtime","render","camera","scene"],"agendas":[{"id":"AGD-0026","file":"workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15"}],"decisions":[{"id":"DEC-0014","file":"workflow/decisions/DEC-0014-frame-composer-render-integration.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_agenda":"AGD-0026"}],"plans":[{"id":"PLN-0017","file":"workflow/plans/PLN-0017-frame-composer-core-and-hardware-ownership.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]},{"id":"PLN-0018","file":"workflow/plans/PLN-0018-sprite-controller-and-frame-emission-model.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14","ref_decisions":["DEC-0014"]},{"id":"PLN-0019","file":"workflow/plans/PLN-0019-scene-binding-camera-and-scene-status.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]},{"id":"PLN-0020","file":"workflow/plans/PLN-0020-cache-refresh-and-render-frame-path.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]},{"id":"PLN-0021","file":"workflow/plans/PLN-0021-service-retirement-callsite-migration-and-regression.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0027","status":"open","ticket":"frame-composer-public-syscall-surface","title":"Agenda - FrameComposer Public Syscall Surface","created_at":"2026-04-17","updated_at":"2026-04-17","tags":["gfx","runtime","syscall","abi","frame-composer","scene","camera","sprites"],"agendas":[{"id":"AGD-0027","file":"workflow/agendas/AGD-0027-frame-composer-public-syscall-surface.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17"}],"decisions":[{"id":"DEC-0015","file":"workflow/decisions/DEC-0015-frame-composer-public-syscall-surface.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_agenda":"AGD-0027"}],"plans":[{"id":"PLN-0022","file":"workflow/plans/PLN-0022-composer-syscall-domain-and-spec-propagation.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]},{"id":"PLN-0023","file":"workflow/plans/PLN-0023-composer-runtime-dispatch-and-legacy-removal.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]},{"id":"PLN-0024","file":"workflow/plans/PLN-0024-composer-cartridge-tooling-and-regression-migration.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]},{"id":"PLN-0025","file":"workflow/plans/PLN-0025-final-ci-validation-and-polish.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0028","status":"open","ticket":"deferred-overlay-and-primitive-composition","title":"Deferred Overlay and Primitive Composition over FrameComposer","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["gfx","runtime","render","frame-composer","overlay","primitives","hud"],"agendas":[{"id":"AGD-0028","file":"workflow/agendas/AGD-0028-deferred-overlay-and-primitive-composition.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18"}],"decisions":[{"id":"DEC-0016","file":"workflow/decisions/DEC-0016-deferred-gfx-overlay-outside-frame-composer.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_agenda":"AGD-0028"}],"plans":[{"id":"PLN-0026","file":"workflow/plans/PLN-0026-gfx-overlay-contract-and-spec-propagation.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]},{"id":"PLN-0027","file":"workflow/plans/PLN-0027-deferred-gfx-overlay-subsystem.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]},{"id":"PLN-0028","file":"workflow/plans/PLN-0028-runtime-frame-end-overlay-integration-and-parity.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]},{"id":"PLN-0029","file":"workflow/plans/PLN-0029-final-overlay-ci-validation-and-polish.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0014","status":"open","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0013","file":"workflow/agendas/AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0016","status":"done","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0015","file":"workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0022","file":"lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} +{"type":"discussion","id":"DSC-0017","status":"done","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0016","file":"workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[{"id":"DEC-0004","file":"workflow/decisions/DEC-0004-asset-entry-metadata-normalization-contract.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09"}],"plans":[],"lessons":[{"id":"LSN-0023","file":"lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} +{"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} +{"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"workflow/agendas/AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"workflow/decisions/DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"workflow/plans/PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} diff --git a/discussion/.backups/index.ndjson.20260418-162925.bak b/discussion/.backups/index.ndjson.20260418-162925.bak new file mode 100644 index 00000000..9ca7cece --- /dev/null +++ b/discussion/.backups/index.ndjson.20260418-162925.bak @@ -0,0 +1,29 @@ +{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":17,"PLN":30,"LSN":32,"CLSN":1}} +{"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"workflow/agendas/AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"workflow/decisions/DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"workflow/plans/PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} +{"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} +{"type":"discussion","id":"DSC-0022","status":"done","ticket":"tile-bank-vs-glyph-bank-domain-naming","title":"Glyph Bank Domain Naming Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["gfx","runtime","naming","domain-model"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0025","file":"lessons/DSC-0022-glyph-bank-domain-naming/LSN-0025-rename-artifact-by-meaning-not-by-token.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} +{"type":"discussion","id":"DSC-0002","status":"open","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0001","file":"workflow/agendas/AGD-0001-runtime-edge-test-plan.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"workflow/agendas/AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0004","status":"open","ticket":"system-run-cart","title":"Agenda - System Run Cart","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0003","file":"workflow/agendas/AGD-0003-system-run-cart.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0005","status":"open","ticket":"system-fault-semantics-and-control-surface","title":"Agenda - System Fault Semantics and Control Surface","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0004","file":"workflow/agendas/AGD-0004-system-fault-semantics-and-control-surface.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0006","status":"open","ticket":"vm-owned-random-service","title":"Agenda - VM-Owned Random Service","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0005","file":"workflow/agendas/AGD-0005-vm-owned-random-service.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0007","status":"open","ticket":"app-home-filesystem-surface-and-semantics","title":"Agenda - App Home Filesystem Surface and Semantics","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0006","file":"workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0008","status":"done","ticket":"perf-runtime-telemetry-hot-path","title":"Agenda - [PERF] Runtime Telemetry Hot Path","created_at":"2026-03-27","updated_at":"2026-04-10","tags":[],"agendas":[{"id":"AGD-0007","file":"workflow/agendas/AGD-0007-perf-runtime-telemetry-hot-path.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0005","file":"workflow/decisions/DEC-0005-perf-push-based-telemetry-model.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0005","file":"workflow/plans/PLN-0005-perf-push-based-telemetry-implementation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0026","file":"lessons/DSC-0008-perf-runtime-telemetry-hot-path/LSN-0026-push-based-telemetry-model.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0009","status":"open","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0008","file":"workflow/agendas/AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0010","status":"open","ticket":"perf-host-desktop-frame-pacing-and-presentation","title":"Agenda - [PERF] Host Desktop Frame Pacing and Presentation","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0009","file":"workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0011","status":"open","ticket":"perf-gfx-render-pipeline-and-dirty-regions","title":"Agenda - [PERF] GFX Render Pipeline and Dirty Regions","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0010","file":"workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0012","status":"open","ticket":"perf-runtime-introspection-syscalls","title":"Agenda - [PERF] Runtime Introspection Syscalls","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0011","file":"workflow/agendas/AGD-0011-perf-runtime-introspection-syscalls.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0013","status":"done","ticket":"perf-host-debug-overlay-isolation","title":"Agenda - [PERF] Host Debug Overlay Isolation","created_at":"2026-03-27","updated_at":"2026-04-10","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0027","file":"lessons/DSC-0013-perf-host-debug-overlay-isolation/LSN-0027-host-debug-overlay-isolation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0024","status":"done","ticket":"generic-memory-bank-slot-contract","title":"Agenda - Generic Memory Bank Slot Contract","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["runtime","asset","memory-bank","slots","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0029","file":"lessons/DSC-0024-generic-memory-bank-slot-contract/LSN-0029-slot-first-bank-telemetry-belongs-in-asset-manager.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0025","status":"done","ticket":"scene-bank-and-viewport-cache-refactor","title":"Scene Bank and Viewport Cache Refactor","created_at":"2026-04-11","updated_at":"2026-04-14","tags":["gfx","tilemap","runtime","render"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0030","file":"lessons/DSC-0025-scene-bank-and-viewport-cache-refactor/LSN-0030-canonical-scene-cache-and-resolver-split.md","status":"done","created_at":"2026-04-14","updated_at":"2026-04-14"}]} +{"type":"discussion","id":"DSC-0026","status":"done","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-18","tags":["gfx","runtime","render","camera","scene"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0031","file":"lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} +{"type":"discussion","id":"DSC-0027","status":"open","ticket":"frame-composer-public-syscall-surface","title":"Agenda - FrameComposer Public Syscall Surface","created_at":"2026-04-17","updated_at":"2026-04-17","tags":["gfx","runtime","syscall","abi","frame-composer","scene","camera","sprites"],"agendas":[{"id":"AGD-0027","file":"workflow/agendas/AGD-0027-frame-composer-public-syscall-surface.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17"}],"decisions":[{"id":"DEC-0015","file":"workflow/decisions/DEC-0015-frame-composer-public-syscall-surface.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_agenda":"AGD-0027"}],"plans":[{"id":"PLN-0022","file":"workflow/plans/PLN-0022-composer-syscall-domain-and-spec-propagation.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]},{"id":"PLN-0023","file":"workflow/plans/PLN-0023-composer-runtime-dispatch-and-legacy-removal.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]},{"id":"PLN-0024","file":"workflow/plans/PLN-0024-composer-cartridge-tooling-and-regression-migration.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]},{"id":"PLN-0025","file":"workflow/plans/PLN-0025-final-ci-validation-and-polish.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0028","status":"open","ticket":"deferred-overlay-and-primitive-composition","title":"Deferred Overlay and Primitive Composition over FrameComposer","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["gfx","runtime","render","frame-composer","overlay","primitives","hud"],"agendas":[{"id":"AGD-0028","file":"workflow/agendas/AGD-0028-deferred-overlay-and-primitive-composition.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18"}],"decisions":[{"id":"DEC-0016","file":"workflow/decisions/DEC-0016-deferred-gfx-overlay-outside-frame-composer.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_agenda":"AGD-0028"}],"plans":[{"id":"PLN-0026","file":"workflow/plans/PLN-0026-gfx-overlay-contract-and-spec-propagation.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]},{"id":"PLN-0027","file":"workflow/plans/PLN-0027-deferred-gfx-overlay-subsystem.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]},{"id":"PLN-0028","file":"workflow/plans/PLN-0028-runtime-frame-end-overlay-integration-and-parity.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]},{"id":"PLN-0029","file":"workflow/plans/PLN-0029-final-overlay-ci-validation-and-polish.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0014","status":"open","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0013","file":"workflow/agendas/AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0016","status":"done","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0015","file":"workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0022","file":"lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} +{"type":"discussion","id":"DSC-0017","status":"done","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0016","file":"workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[{"id":"DEC-0004","file":"workflow/decisions/DEC-0004-asset-entry-metadata-normalization-contract.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09"}],"plans":[],"lessons":[{"id":"LSN-0023","file":"lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} +{"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} +{"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"workflow/agendas/AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"workflow/decisions/DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"workflow/plans/PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 68838273..6c1c7a0d 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,4 +1,4 @@ -{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":17,"PLN":30,"LSN":31,"CLSN":1}} +{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":17,"PLN":30,"LSN":34,"CLSN":1}} {"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"workflow/agendas/AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"workflow/decisions/DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"workflow/plans/PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} {"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} @@ -18,9 +18,9 @@ {"type":"discussion","id":"DSC-0013","status":"done","ticket":"perf-host-debug-overlay-isolation","title":"Agenda - [PERF] Host Debug Overlay Isolation","created_at":"2026-03-27","updated_at":"2026-04-10","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0027","file":"lessons/DSC-0013-perf-host-debug-overlay-isolation/LSN-0027-host-debug-overlay-isolation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0024","status":"done","ticket":"generic-memory-bank-slot-contract","title":"Agenda - Generic Memory Bank Slot Contract","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["runtime","asset","memory-bank","slots","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0029","file":"lessons/DSC-0024-generic-memory-bank-slot-contract/LSN-0029-slot-first-bank-telemetry-belongs-in-asset-manager.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0025","status":"done","ticket":"scene-bank-and-viewport-cache-refactor","title":"Scene Bank and Viewport Cache Refactor","created_at":"2026-04-11","updated_at":"2026-04-14","tags":["gfx","tilemap","runtime","render"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0030","file":"lessons/DSC-0025-scene-bank-and-viewport-cache-refactor/LSN-0030-canonical-scene-cache-and-resolver-split.md","status":"done","created_at":"2026-04-14","updated_at":"2026-04-14"}]} -{"type":"discussion","id":"DSC-0026","status":"open","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-15","tags":["gfx","runtime","render","camera","scene"],"agendas":[{"id":"AGD-0026","file":"workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15"}],"decisions":[{"id":"DEC-0014","file":"workflow/decisions/DEC-0014-frame-composer-render-integration.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_agenda":"AGD-0026"}],"plans":[{"id":"PLN-0017","file":"workflow/plans/PLN-0017-frame-composer-core-and-hardware-ownership.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]},{"id":"PLN-0018","file":"workflow/plans/PLN-0018-sprite-controller-and-frame-emission-model.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14","ref_decisions":["DEC-0014"]},{"id":"PLN-0019","file":"workflow/plans/PLN-0019-scene-binding-camera-and-scene-status.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]},{"id":"PLN-0020","file":"workflow/plans/PLN-0020-cache-refresh-and-render-frame-path.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]},{"id":"PLN-0021","file":"workflow/plans/PLN-0021-service-retirement-callsite-migration-and-regression.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]}],"lessons":[]} -{"type":"discussion","id":"DSC-0027","status":"open","ticket":"frame-composer-public-syscall-surface","title":"Agenda - FrameComposer Public Syscall Surface","created_at":"2026-04-17","updated_at":"2026-04-17","tags":["gfx","runtime","syscall","abi","frame-composer","scene","camera","sprites"],"agendas":[{"id":"AGD-0027","file":"workflow/agendas/AGD-0027-frame-composer-public-syscall-surface.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17"}],"decisions":[{"id":"DEC-0015","file":"workflow/decisions/DEC-0015-frame-composer-public-syscall-surface.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_agenda":"AGD-0027"}],"plans":[{"id":"PLN-0022","file":"workflow/plans/PLN-0022-composer-syscall-domain-and-spec-propagation.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]},{"id":"PLN-0023","file":"workflow/plans/PLN-0023-composer-runtime-dispatch-and-legacy-removal.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]},{"id":"PLN-0024","file":"workflow/plans/PLN-0024-composer-cartridge-tooling-and-regression-migration.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]},{"id":"PLN-0025","file":"workflow/plans/PLN-0025-final-ci-validation-and-polish.md","status":"accepted","created_at":"2026-04-17","updated_at":"2026-04-17","ref_decisions":["DEC-0015"]}],"lessons":[]} -{"type":"discussion","id":"DSC-0028","status":"open","ticket":"deferred-overlay-and-primitive-composition","title":"Deferred Overlay and Primitive Composition over FrameComposer","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["gfx","runtime","render","frame-composer","overlay","primitives","hud"],"agendas":[{"id":"AGD-0028","file":"workflow/agendas/AGD-0028-deferred-overlay-and-primitive-composition.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18"}],"decisions":[{"id":"DEC-0016","file":"workflow/decisions/DEC-0016-deferred-gfx-overlay-outside-frame-composer.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_agenda":"AGD-0028"}],"plans":[{"id":"PLN-0026","file":"workflow/plans/PLN-0026-gfx-overlay-contract-and-spec-propagation.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]},{"id":"PLN-0027","file":"workflow/plans/PLN-0027-deferred-gfx-overlay-subsystem.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]},{"id":"PLN-0028","file":"workflow/plans/PLN-0028-runtime-frame-end-overlay-integration-and-parity.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]},{"id":"PLN-0029","file":"workflow/plans/PLN-0029-final-overlay-ci-validation-and-polish.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-18","ref_decisions":["DEC-0016"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0026","status":"done","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-18","tags":["gfx","runtime","render","camera","scene"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0031","file":"lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} +{"type":"discussion","id":"DSC-0027","status":"done","ticket":"frame-composer-public-syscall-surface","title":"Agenda - FrameComposer Public Syscall Surface","created_at":"2026-04-17","updated_at":"2026-04-18","tags":["gfx","runtime","syscall","abi","frame-composer","scene","camera","sprites"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0032","file":"lessons/DSC-0027-frame-composer-public-syscall-surface/LSN-0032-public-abi-must-follow-the-canonical-service-boundary.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} +{"type":"discussion","id":"DSC-0028","status":"done","ticket":"deferred-overlay-and-primitive-composition","title":"Deferred Overlay and Primitive Composition over FrameComposer","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["gfx","runtime","render","frame-composer","overlay","primitives","hud"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0033","file":"lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} {"type":"discussion","id":"DSC-0014","status":"open","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0013","file":"workflow/agendas/AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0016","status":"done","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0015","file":"workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0022","file":"lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} diff --git a/discussion/lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md b/discussion/lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md new file mode 100644 index 00000000..ace30888 --- /dev/null +++ b/discussion/lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md @@ -0,0 +1,74 @@ +--- +id: LSN-0031 +ticket: render-all-scene-cache-and-camera-integration +title: Frame Composition Belongs Above the Render Backend +created: 2026-04-18 +tags: [gfx, runtime, render, camera, scene, sprites, frame-composer] +--- + +## Context + +`DSC-0025` split canonical scene ownership from viewport caching and resolver policy, but the runtime still treated `Gfx.render_all()` as the operational frame entrypoint. That left scene binding, camera state, cache refresh, and sprite submission spread across the wrong layer. + +`DSC-0026` completed the integration by making `FrameComposer` the owner of frame orchestration and reducing `Gfx` to a backend that consumes already prepared render state. + +## Key Decisions + +### Frame Orchestration Must Not Live in the Backend + +**What:** +`FrameComposer.render_frame()` became the canonical frame service, while `Gfx.render_all()` was retired from the runtime-facing flow. + +**Why:** +The backend should execute composition, not decide scene binding, camera policy, cache refresh, or sprite lifecycle. Keeping those responsibilities above `Gfx` preserves a cleaner ownership model and avoids re-entangling policy with raster code. + +**Trade-offs:** +This adds an explicit orchestration layer between runtime callsites and the renderer, but the resulting boundaries are easier to evolve and test. + +### Scene Binding, Camera, Cache, and Sprites Form One Operational Unit + +**What:** +`FrameComposer` owns: +- active scene binding by bank id and shared scene reference; +- camera coordinates in top-left world pixel space; +- `SceneViewportCache` and `SceneViewportResolver`; +- a frame-emission `SpriteController`. + +**Why:** +These concerns all define what a frame is. Splitting them across multiple owners would recreate stale-state bugs and make no-scene behavior ambiguous. + +**Trade-offs:** +The composer becomes a richer subsystem, but it carries policy in one place instead of leaking it into unrelated APIs. + +### The World Path Must Stay Tile-Size Agnostic + +**What:** +The integrated frame path derives cache sizing, resolver math, and world-copy behavior from per-layer scene metadata instead of a hard-coded `16x16` assumption. + +**Why:** +The scene contract already allows canonical `8x8`, `16x16`, and `32x32` tile sizes. The frame service has to consume that contract faithfully or it becomes a hidden compatibility break. + +**Trade-offs:** +The integration and tests need to exercise more than the legacy default path, but the renderer no longer bakes in a false invariant. + +## Patterns and Algorithms + +- Put frame policy in a dedicated orchestration layer and keep the renderer backend-oriented. +- Treat scene binding, camera state, cache lifetime, and sprite submission as one cohesive frame model. +- Refresh cache state inside the orchestrator before composition instead of letting the renderer discover refresh policy. +- Prefer frame-emission sprite submission with internal ordering over caller-owned sprite slots. +- Keep the no-scene path valid so world composition remains optional, not mandatory. + +## Pitfalls + +- Leaving `render_all()` alive as a canonical path creates a fragile dual-service model. +- Letting `Gfx` own cache refresh semantics collapses the boundary between policy and execution. +- Requiring a scene for every frame quietly breaks sprite-only or fade-only operation. +- Testing only `16x16` scenes hides regressions against valid `8x8` or `32x32` content. + +## Takeaways + +- Frame composition belongs in a subsystem that owns policy, not in the backend that draws pixels. +- Scene binding, camera, cache, resolver, and sprite submission should converge under one frame owner. +- No-scene rendering is part of the contract and should stay valid throughout integration work. +- Tile-size assumptions must be derived from canonical scene metadata, never from renderer habit. diff --git a/discussion/lessons/DSC-0027-frame-composer-public-syscall-surface/LSN-0032-public-abi-must-follow-the-canonical-service-boundary.md b/discussion/lessons/DSC-0027-frame-composer-public-syscall-surface/LSN-0032-public-abi-must-follow-the-canonical-service-boundary.md new file mode 100644 index 00000000..c0012645 --- /dev/null +++ b/discussion/lessons/DSC-0027-frame-composer-public-syscall-surface/LSN-0032-public-abi-must-follow-the-canonical-service-boundary.md @@ -0,0 +1,56 @@ +--- +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. diff --git a/discussion/lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md b/discussion/lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md new file mode 100644 index 00000000..76a16cc1 --- /dev/null +++ b/discussion/lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md @@ -0,0 +1,56 @@ +--- +id: LSN-0033 +ticket: deferred-overlay-and-primitive-composition +title: Debug Primitives Should Be a Final Overlay, Not Part of Game Composition +created: 2026-04-18 +tags: [gfx, runtime, render, frame-composer, overlay, primitives, hud, debug] +--- + +## Context + +After `FrameComposer.render_frame()` became the canonical game-frame entrypoint, immediate `gfx.*` primitive writes were no longer stable. Scene-backed composition could rebuild the framebuffer after `draw_text(...)` or other debug primitives had already written to it. + +`DSC-0028` resolved that conflict by moving `gfx.*` primitives into a deferred overlay/debug stage outside `FrameComposer`, drained only after canonical game composition and fades are complete. + +## Key Decisions + +### Debug Overlay Must Stay Outside the Canonical Game Pipeline + +**What:** +`FrameComposer` keeps ownership of canonical game composition, while debug/text/primitive commands are captured separately and drained later as a final overlay. + +**Why:** +Game composition and debug overlay have different purposes. The first must remain canonical and deterministic; the second must remain opportunistic, screen-space, and independent from scene or sprite semantics. + +**Trade-offs:** +The renderer needs a second deferred path, but the game pipeline no longer depends on transient debug state. + +### Final Visual Ordering Matters More Than Immediate Writes + +**What:** +Overlay/debug commands are drained after scene composition, sprite composition, and fades, with parity between scene-bound and no-scene frame paths. + +**Why:** +The stable user-visible contract is that debug primitives appear on top. Immediate writes were only an implementation detail, and they stopped preserving that contract once frame composition became deferred and canonical. + +**Trade-offs:** +This changes primitive semantics from "write now" to "show at frame end," but it produces the behavior users actually rely on. + +## Patterns and Algorithms + +- Separate canonical composition state from debug-overlay state even when both reuse the same raster backend. +- Capture primitives as commands first, then drain them at the final stage where visual priority is unambiguous. +- Preserve the same overlay semantics whether a scene is bound or not. +- Keep implementation reuse internal while maintaining a clear semantic boundary in the public model. + +## Pitfalls + +- Treating debug primitives as part of HUD or scene composition will eventually couple tooling/debug behavior to gameplay pipeline rules. +- Draining overlay before fades or before final frame composition breaks the visible "always on top" contract. +- Reusing `FrameComposer` storage for overlay state collapses the ownership split that prevents these bugs. + +## Takeaways + +- Immediate framebuffer writes are not a reliable contract once final composition is orchestrated elsewhere. +- Debug primitives work best as a dedicated final overlay layer. +- Ownership separation is what keeps debug behavior stable while the canonical render pipeline evolves. diff --git a/discussion/workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md b/discussion/workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md deleted file mode 100644 index 756daaea..00000000 --- a/discussion/workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md +++ /dev/null @@ -1,405 +0,0 @@ ---- -id: AGD-0026 -ticket: render-all-scene-cache-and-camera-integration -title: Agenda - Integrate render_all with Scene Cache and Camera -status: accepted -created: 2026-04-14 -updated: 2026-04-15 -tags: [gfx, runtime, render, camera, scene] ---- - -## Contexto - -A thread `DSC-0025` fechou a base arquitetural para `SceneBank`, `SceneViewportCache`, `SceneViewportResolver` e o decoder binário de `SCENE`. O renderer já possui um caminho explícito `render_scene_from_cache(&SceneViewportCache, &ResolverUpdate)`, mas o loop operacional do runtime ainda chama apenas `render_all()`. - -Hoje, em [tick.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs:148), o frame segue pelo `hw.gfx_mut().render_all()`. Isso significa que o caminho novo de world render ainda não está integrado ao ciclo normal do runtime. - -Ao mesmo tempo, a integração correta depende de fechar o contrato mínimo da câmera, porque o `SceneViewportResolver` já assume uma posição de câmera em pixel space e produz `ResolverUpdate` a partir dela. Sem essa integração, o runtime fica com duas verdades práticas: - -- a arquitetura aceita para world rendering; -- o caminho ainda efetivamente usado pelo frame loop. - -## Problema - -Precisamos integrar `render_scene_from_cache()` ao `render_all()` e ao ciclo real do runtime sem reabrir a arquitetura já aceita para `SceneBank` / `SceneViewportCache` / `SceneViewportResolver`. - -O problema concreto não é só “chamar uma função”. É decidir: - -- quem é dono do estado de câmera mínimo; -- onde `SceneBank`, `SceneViewportCache` e `SceneViewportResolver` passam a residir em runtime; -- quando o cache é atualizado; -- como o `render_all()` deixa de ser um caminho “scene-blind” e vira o entrypoint normal da composição final. - -## Pontos Criticos - -- `render_all()` deve continuar funcional mesmo quando nenhuma scene estiver carregada. -- O `render_all()` atual não desenha world layers; ele só compõe sprites de prioridade 0 e fades. -- O modelo atual de `Sprite.priority` mistura duas responsabilidades: - - em qual faixa de composição o sprite entra; - - qual a ordem relativa entre sprites naquela faixa. -- `render_scene_from_cache()` existe, mas exige `SceneViewportCache` e `ResolverUpdate` já preparados por fora. -- O modelo atual de sprites ainda é slot-first para o chamador: - - há armazenamento fixo; - - o dev informa índice; - - e o renderer precisa filtrar `active`. -- O `SceneViewportResolver` já carrega política importante: - - câmera em pixel space - - anchors - - clamp - - histerese - - refresh requests - - copy requests -- Ainda não existe um dono explícito do estado operacional: - - cena ativa - - cache ativo - - resolver ativo - - câmera ativa -- O `FrameComposer` não pode regredir o contrato já aceito no scene model: - - `SceneLayer.tile_size` já é por-layer e aceita `8x8`, `16x16` e `32x32`; - - o decoder de `SCENE` já materializa esses tamanhos; - - fixar o pipeline em `16x16` dentro do orquestrador de frame criaria uma restrição artificial que não existe no modelo canônico. -- Se a integração for mal feita, o renderer pode voltar a misturar: - - política de câmera - - atualização de cache - - composição final - -## Opcoes - -### Opcao 1 - Integrar tudo diretamente dentro de `Gfx` - -**Como seria:** -`Gfx` passa a possuir a scene ativa, o cache, o resolver e a câmera; `render_all()` atualiza resolver/cache e já compõe tudo. - -**Vantagens:** -- caminho curto de integração; -- menos objetos atravessando o runtime; -- fácil de chamar a partir do tick. - -**Desvantagens:** -- empurra para `Gfx` responsabilidade demais; -- mistura composição com estado de cena/câmera; -- reduz clareza para testes e evolução futura. - -### Opcao 2 - Integrar no runtime com um controlador explícito de scene viewport - -**Como seria:** -O runtime ou um pequeno controlador operacional passa a possuir: -- scene ativa -- cache -- resolver -- câmera - -Esse controlador atualiza o cache quando a câmera muda e entrega ao `Gfx` apenas o que ele precisa para compor. - -**Vantagens:** -- separa melhor estado operacional de composição; -- mantém `Gfx` mais focado em render; -- preserva a ideia de que o resolver é dono da política de movimento/rematerialização. -- permite manter um caminho explícito de `render_all()` sem scene carregada. - -**Desvantagens:** -- adiciona mais um objeto operacional no runtime; -- exige definir uma superfície clara entre runtime e renderer. - -### Opcao 3 - Fazer uma integração mínima temporária em `render_all()` e postergar a arquitetura do dono da câmera - -**Como seria:** -Criar um caminho temporário para que `render_all()` receba ou consulte estado suficiente para chamar `render_scene_from_cache()`, mas sem ainda fechar onde mora a câmera a longo prazo. - -**Vantagens:** -- acelera a ligação do caminho novo ao frame loop; -- destrava testes end-to-end rapidamente. - -**Desvantagens:** -- alto risco de solução transitória virar definitiva; -- deixa ambiguidade operacional exatamente no ponto mais sensível da integração. - -## Sugestao / Recomendacao - -Seguir com a **Opcao 2**. - -Ou seja: - -- `FrameComposer` passa a ser o orquestrador de frame/scene; -- `FrameComposer` deve morar em `hardware/drivers`; -- `Hardware` passa a agregar `FrameComposer` ao lado de `Gfx`; -- `Gfx` permanece como backend de composição e blit; -- a política do frame não deve ficar presa ao hardware atual; -- isso preserva espaço para: - - fast paths com diretivas/capacidades de GPU quando existirem; - - uma futura implementação mais próxima de PPU / bare metal; -- `render_all()` deve continuar sendo o entrypoint normal de composição; -- `render_all()` deve continuar funcionando mesmo sem scene ativa; -- mas ele não deve virar dono da câmera nem do ciclo de atualização do cache; -- precisamos de um orquestrador operacional no runtime, ou imediatamente adjacente a ele, que: - - mantenha a scene ativa opcional; - - mantenha a câmera / viewport mínima; - - mantenha o controlador de sprites do frame; - - atualize o `SceneViewportResolver` quando houver scene; - - aplique refreshes ao `SceneViewportCache` quando houver scene; - - e entregue ao `Gfx` o estado pronto para compor. - -Mais explicitamente: - -- `FrameComposer` passa a ser dono de: - - scene ativa; - - câmera / viewport; - - `SceneViewportCache`; - - `SceneViewportResolver`; - - sprites emitidos no frame; -- o state de scene/sprite que hoje esteja em `Gfx` deve migrar para `FrameComposer`; -- `Gfx` deve ficar focado em: - - composição; - - blit; - - raster; - - execução visual do frame preparado. - -Para V1, o contrato mínimo de câmera pode continuar pequeno: - -- `camera_x_px: i32` -- `camera_y_px: i32` -- representando o canto superior esquerdo da viewport no mundo - -Sem follow/smoothing/shake/cut nesta etapa. - -O comportamento mínimo recomendado fica: - -- sem scene ativa: - - `FrameComposer` continua válido; - - `render_all()` compõe apenas o que já existe fora do pipeline de world (`sprites`, `fades`, e futuramente `HUD` quando aplicável); - - não existe `clear` implícito; - - limpar o `back` continua sendo responsabilidade explícita do chamador / dev; -- com scene ativa: - - `FrameComposer` atualiza resolver/cache; - - `render_all()` compõe o world a partir do cache e preserva a ordem já aceita. - -Esta direção é provisoriamente aceita mesmo sem a figura final completa, justamente para permitir que a integração avance e revele os pontos onde a separação runtime/backend ainda precise de ajuste. - -Para sprites, a direção provisória recomendada fica: - -- cada `Sprite` deve carregar: - - `layer` - - `priority` -- `Sprite.active` deve ser removido; -- `layer` define em qual faixa de composição o sprite entra; -- `priority` define a ordenação entre sprites daquela mesma faixa; -- a composição observável passa a ser por camada: - - `(sprites -> scene) layer_0` - - `(sprites -> scene) layer_1` - - `(sprites -> scene) layer_2` - - `(sprites -> scene) layer_3` - -Isso substitui o modelo atual em que um único `priority` tenta representar ao mesmo tempo posição macro na composição e ordenação fina. - -O modelo operacional recomendado para sprites passa a ser: - -- capacidade máxima interna de `512` sprites por frame; -- contador zerado a cada frame; -- o dev não informa mais índice de sprite; -- cada emissão ocupa o próximo slot interno disponível; -- o registro já coloca o sprite no bucket correto da layer; -- a composição consome apenas os sprites emitidos naquele frame. - -## Perguntas em Aberto - -- Fechado provisoriamente: - - `FrameComposer` em `hardware/drivers`; - - `Hardware` agrega `FrameComposer` e `Gfx`; - - `Gfx` atua como backend operacional de composição. -- O contrato mínimo do `FrameComposer` precisa ser fechado normativamente. -- Fechado: - - o subsistema interno de sprites se chama `SpriteController`. -- `Sprite.layer` deve ser um enum fechado (`Layer0..Layer3`) ou um tipo mais genérico? - - fechado provisoriamente: - - manter numérico; - - usar o mesmo tipo/referência de layer do `SceneBank`. -- A composição por camada deve ser: - - `sprites -> scene` dentro de cada layer, como direção inicial, - - ou `scene -> sprites` para alguma camada específica? -- A ordenação entre sprites de uma mesma layer será: - - fechado: - - `priority` menor blita primeiro; - - em empate, FIFO por ordem de registro. -- Overflow de sprite no frame: - - fechado: excedentes são ignorados; - - deve existir espaço para log/telemetria; - - futuramente isso pode virar sinal negativo para certificação. -- `emit_sprite(...)` precisa retornar algo, ou ter reset separado além de `begin_frame()`? - - fechado por enquanto: - - não; - - usar apenas log do sistema para overflow/eventos operacionais; - - não introduzir reset extra além do fluxo normal do frame. -- `render_all()` deve: - - continuar sem parâmetros e consultar estado já preparado, - - ou ganhar uma nova superfície interna para receber o scene state preparado? - - direção aceita: - - `FrameComposer` chama o entrypoint de composição do backend visual; - - `Gfx.render_all()` deve morrer; - - o serviço deve migrar para `FrameComposer.renderFrame()`. - -## Contrato Minimo Proposto - -Direção proposta para V1 do `FrameComposer`: - -- `bind_scene(...)` - - recebe um `scene bank id`; - - `FrameComposer` deve possuir acesso a `SceneBankPoolAccess`; - - resolve a scene ativa através do pool; - - o acesso ao bank deve ser sempre por ponteiro / referência compartilhada, nunca por cópia; - - ao bindar, o compositor guarda: - - `scene_bank_id`; - - `Arc` já resolvido; - - consegue verificar se a scene está carregada; - - inicializa ou reinicializa cache/resolver conforme necessário. - -- `unbind_scene()` - - remove a scene ativa; - - invalida o pipeline de world; - - descarta o cache associado à scene bindada; - - mantém o compositor funcional para `sprites + fades`. - -- `set_camera(x, y)` - - atualiza a posição da câmera em pixel space; - - `x, y` representam o canto superior esquerdo da viewport no mundo. - -- `begin_frame()` - - zera o contador de sprites emitidos; - - limpa buckets internos de sprite; - - prepara o estado transitório do frame. - -- `emit_sprite(...)` - - registra um sprite no próximo slot interno disponível; - - associa o sprite à sua `layer`; - - insere no bucket correspondente; - - overflow é ignorado com espaço para log/telemetria. - -- `compose_frame()` - - se houver scene ativa: - - atualiza `SceneViewportResolver`; - - aplica `CacheRefreshRequest`s ao `SceneViewportCache`; - - aciona o caminho de composição world + sprites; - - se não houver scene ativa: - - aciona o caminho `sprites + fades`; - - delega a composição efetiva ao `Gfx`. - -### Observacoes - -- `end_frame()` não parece obrigatório na V1. -- `begin_frame()` + `compose_frame()` já cobrem o ciclo mínimo. -- `FrameComposer` decide e prepara; - `Gfx` executa a composição. -- o binding de scene deve ser por `scene bank id`, não por ownership direto de `SceneBank`. -- o `SceneViewportCache` vive dentro do `FrameComposer` enquanto a scene estiver bindada. -- troca do conteúdo do slot/bank exige novo `bind_scene(...)`; - o `FrameComposer` não deve ficar fazendo polling constante do pool para revalidar a scene ativa. -- o fluxo operacional aceito é: - - `FrameComposer.compose_frame()` - - chama o serviço `FrameComposer.renderFrame()`. -- `FrameComposer` deve ser capaz de renderizar algo 100% do tempo: - - cache/resolver ficam `None` sem bind; - - deve existir uma forma explícita de saber se a scene está disponível para render. -- `bind_scene(...)` substitui completamente a scene anterior. - -## Sugestao / Recomendacao Atualizada - -Aceitar o contrato mínimo acima como base de fechamento da agenda, a menos que apareça alguma necessidade concreta de: - -- separar `compose_frame()` em múltiplas fases públicas; -- expor refresh manual de cache para o chamador; -- ou introduzir um `end_frame()` com semântica real além do reset que já ocorre em `begin_frame()`. -- manter o binding de scene como: - - `scene_bank_id + Arc`; - - com rebind explícito quando o slot mudar. -- Quem é responsável por aplicar `CacheRefreshRequest` ao `SceneViewportCache`: - - fechado: sempre o `FrameComposer`. -- Qual é o contrato explícito de “nenhuma scene carregada”: - - fechado: `sprites + fades`, sem `clear` implícito. -- Como a cena ativa é selecionada e trocada no ciclo real: - - fechado: `bind_scene(scene_bank_id)` com resolução através de `SceneBankPoolAccess`. -- O HUD entra nesta integração já agora, ou o foco da primeira integração é apenas world + sprites + fades? - - fechado: sem HUD nesta primeira integração. - -## Reabertura 2026-04-15 - -### Contexto adicional - -Ao revisitar a thread, apareceu uma restrição indevida: tratar o `FrameComposer` como se aceitasse apenas tilesets `16x16`. - -Isso conflita com o estado atual do runtime: - -- `TileSize` no HAL já enumera `Size8`, `Size16` e `Size32`; -- `SceneLayer` carrega `tile_size` por layer; -- o decoder de `SCENE` já aceita `8`, `16` e `32`; -- `SceneViewportResolver` e `SceneViewportCache` já calculam offsets, anchors e cópia a partir do `tile_size` da própria layer. - -O risco aqui não é apenas de implementação. Se o contrato do `FrameComposer` assumir `16x16` como pré-condição, ele quebra a neutralidade do orquestrador e reabre uma limitação artificial acima do scene model. - -### Problema reaberto - -Precisamos fechar explicitamente que o `FrameComposer` aceita cenas/layers com `tile_size` `8x8` e não impõe `16x16` como tamanho mínimo ou obrigatório para o world path. - -### Opcoes adicionais - -### Opcao 4 - Fixar `16x16` no `FrameComposer` e tratar `8x8` como fora de escopo - -**Vantagens:** -- reduz casos de teste imediatos; -- simplifica implementação inicial se alguém estiver assumindo viewport/caches calibrados manualmente para `16`. - -**Desvantagens:** -- contradiz o scene model já aceito; -- introduz restrição artificial no orquestrador; -- obriga futura revisão de contrato para reaceitar algo que a base já suporta. - -### Opcao 5 - Manter `FrameComposer` tile-size agnostic e aceitar `8x8` desde V1 - -**Vantagens:** -- preserva o contrato canônico já existente em `SceneLayer`; -- mantém o `FrameComposer` como orquestrador, não como redefinidor de formato; -- evita bifurcação entre pipeline de scene e pipeline de composição. - -**Desvantagens:** -- exige deixar isso explícito na decisão e nos planos; -- aumenta a exigência de testes para viewport/cache/cópia com `8x8`. - -### Recomendacao adicional - -Seguir com a **Opcao 5**. - -Norma proposta para fechamento desta reabertura: - -- `FrameComposer` deve aceitar scenes/layers cujo `tile_size` resolvido seja `8x8`, `16x16` ou `32x32`; -- `FrameComposer` nao deve impor `16x16` como pré-condição para bind, cache, resolver ou composição; -- qualquer validação de compatibilidade deve ser derivada do `tile_size` declarado pela própria layer / glyph bank, nunca de um default rígido no compositor; -- os planos derivados desta thread precisam citar testes explícitos para `8x8`. - -## Criterio para Encerrar - -Esta agenda pode ser encerrada quando estiver explícito: - -- quem é dono do estado mínimo de câmera; -- quem é dono da scene/cache/resolver ativos; -- como funciona o bind/unbind da scene ativa; -- quando o cache é atualizado; -- como `render_all()` passa a compor o world path aceito; -- que o `FrameComposer` permanece agnóstico ao `tile_size` canônico da layer e aceita `8x8` sem downgrade contratual; -- e qual é a superfície mínima de integração para implementação sem reabrir a arquitetura base. - -## Resolucao - -Esta agenda fica aceita com a seguinte direcao: - -- `Gfx.render_all()` deve ser aposentado; -- o fluxo operacional deve convergir para `FrameComposer.render_frame()`; -- `FrameComposer` vive em `hardware/drivers`, ao lado de `Gfx`, e passa a ser dono do estado operacional do frame; -- `FrameComposer` deve manter: - - scene ativa opcional; - - camera/viewport; - - `SceneViewportCache`; - - `SceneViewportResolver`; - - `SpriteController`; -- scene ativa e acessada por `scene_bank_id + Arc` via `SceneBankPoolAccess`, sem copias; -- troca de slot exige novo `bind_scene(...)`; -- sem scene ativa, o frame continua valido com `sprites + fades`, sem `clear` implicito; -- sprites passam a ser emitidos por frame, sem `Sprite.active`, com capacidade maxima de `512`, overflow ignorado e ordenacao por `layer`, `priority`, e FIFO em empate; -- HUD fica fora desta primeira integracao. diff --git a/discussion/workflow/agendas/AGD-0027-frame-composer-public-syscall-surface.md b/discussion/workflow/agendas/AGD-0027-frame-composer-public-syscall-surface.md deleted file mode 100644 index 560720cd..00000000 --- a/discussion/workflow/agendas/AGD-0027-frame-composer-public-syscall-surface.md +++ /dev/null @@ -1,214 +0,0 @@ ---- -id: AGD-0027 -ticket: frame-composer-public-syscall-surface -title: Agenda - FrameComposer Public Syscall Surface -status: accepted -created: 2026-04-17 -updated: 2026-04-17 -tags: [gfx, runtime, syscall, abi, frame-composer, scene, camera, sprites] ---- - -## Contexto - -`DEC-0014` e os planos `PLN-0017` a `PLN-0021` fecharam a migração interna do pipeline de frame para `FrameComposer`: - -- `FrameComposer` virou o orquestrador canônico do frame; -- `Hardware` passou a agregá-lo ao lado de `Gfx`; -- scene, camera, cache, resolver e sprite emission migraram para ownership interno dele; -- o frame loop do runtime passou a renderizar via `FrameComposer.render_frame()`. - -Isso resolveu a base operacional interna, mas não fechou a superfície pública equivalente para a VM. A ABI pública ainda expõe apenas o contrato legado de `gfx.set_sprite(...)`, enquanto `bind_scene(...)` e `set_camera(...)` existem apenas como APIs internas do driver. - -Na prática, hoje temos uma assimetria: - -- a base canônica do frame está em `FrameComposer`; -- mas a ABI pública ainda não trata `FrameComposer` como serviço canônico para scene, camera e sprites. - -Essa lacuna impede a migração do restante da stack e também impede um stress cartridge que atravesse de verdade o pipeline novo por syscall pública. - -## Problema - -Precisamos definir a nova superfície pública de syscall para o pipeline canônico de `FrameComposer` sem reabrir a decisão já aceita sobre ownership interno do frame. - -O problema concreto não é “adicionar 2 ou 3 syscalls”. Precisamos decidir: - -- quais operações de `FrameComposer` viram ABI pública agora; -- se `gfx.set_sprite(...)` continua como shim legado ou perde status canônico; -- qual é o contrato mínimo de scene/camera que a VM pode observar/controlar; -- como nomear e versionar essa superfície pública sem criar um segundo modelo canônico concorrente; -- qual é a estratégia de transição para cartridge, runtime tests e stress tests; -- como propagar essa mudança para a spec canônica e, se necessário, para contratos de ABI e `ISA_CORE`. - -## Pontos Criticos - -- `DEC-0014` já fechou `FrameComposer` como base canônica interna; esta agenda não deve reabrir isso. -- A ABI pública atual ainda expõe `gfx.set_sprite(...)` com semântica herdada de índice/slot, mesmo que a implementação interna já use frame emission. -- `bind_scene(scene_bank_id)` e `set_camera(x, y)` já existem no driver, mas ainda não existem como syscalls públicas. -- Se a nova ABI expuser demais logo de início, vamos congelar cedo demais detalhes que ainda não provaram valor operacional. -- Se a nova ABI expuser de menos, manteremos um modelo híbrido por tempo demais: - - canônico internamente via `FrameComposer`; - - legado externamente via `Gfx`/`set_sprite`. -- Precisamos decidir se o namespace público continua em `gfx.*` por estabilidade do domínio, ou se devemos introduzir algo como `frame.*`. -- A transição precisa preservar compatibilidade suficiente para não quebrar cartridges e testes existentes antes da migração do restante. -- O contrato de sprite precisa deixar claro se o chamador ainda informa índice, se informa `layer`, e se `active` continua existindo na superfície pública. -- A mudança não pode ficar só em código/runtime; a spec canônica precisa ser atualizada para refletir o novo serviço público. -- Se o contrato público afetar superfícies documentadas de ABI ou o material de `ISA_CORE`, essa propagação precisa ser tratada como parte da mesma thread, não como follow-up solto. - -## Opcoes - -### Opcao 1 - Expor um núcleo mínimo canônico em `gfx.*` - -**Como seria:** -Adicionar apenas a superfície mínima para a VM controlar o pipeline novo: - -- `gfx.bind_scene(bank_id)` -- `gfx.unbind_scene()` -- `gfx.set_camera(x, y)` -- `gfx.emit_sprite(...)` - -`gfx.set_sprite(...)` permaneceria por um período como shim legado de compatibilidade. - -**Vantagens:** -- fecha rapidamente a lacuna operacional; -- habilita stress real do pipeline novo; -- reduz o tempo de convivência entre modelo canônico e legado; -- mantém o domínio público em `gfx`, evitando churn de namespace. - -**Desvantagens:** -- introduz ABI nova que precisará de migração coordenada; -- exige definir `emit_sprite(...)` com cuidado para não herdar sem querer o modelo de slot. - -### Opcao 2 - Expor scene/camera agora e adiar o contrato novo de sprite - -**Como seria:** -Publicar apenas: - -- `gfx.bind_scene(bank_id)` -- `gfx.unbind_scene()` -- `gfx.set_camera(x, y)` - -Sprites continuariam publicamente via `gfx.set_sprite(...)` até uma segunda fase. - -**Vantagens:** -- menor mudança imediata de ABI; -- desbloqueia o stress do world path e da câmera; -- reduz o volume inicial da migração pública. - -**Desvantagens:** -- mantém dois modelos públicos de sprite por mais tempo; -- prolonga a semântica de compatibilidade do syscall legado; -- adia exatamente uma das partes centrais da migração para `FrameComposer`. - -### Opcao 3 - Criar um novo namespace público separado, como `composer.*` - -**Como seria:** -O pipeline novo ganha syscalls em um domínio separado, por exemplo: - -- `composer.bind_scene` -- `composer.unbind_scene` -- `composer.set_camera` -- `composer.emit_sprite` - -`gfx.*` ficaria como superfície legacy/low-level. - -**Vantagens:** -- deixa explícita a mudança de serviço canônico; -- evita sobrecarregar semanticamente `gfx`. - -**Desvantagens:** -- adiciona churn conceitual e de nomenclatura; -- fragmenta demais a superfície pública neste momento; -- cria um custo de transição maior sem benefício operacional evidente. - -## Sugestao / Recomendacao - -Seguir com a **Opcao 3**. - -Direção recomendada: - -- a superfície pública canônica deve migrar para o domínio `composer.*`; -- `FrameComposer` vira a base canônica também na ABI pública, com namespace próprio em vez de continuar semanticamente preso a `gfx.*`; -- o núcleo mínimo público deve ser: - - `composer.bind_scene(bank_id) -> status` - - `composer.unbind_scene()` - - `composer.set_camera(x, y)` - - `composer.emit_sprite(...) -> status` -- `gfx.set_sprite(...)` deve morrer e ser removido completamente do contrato público. - -Para sprites, a recomendação provisória é: - -- a nova ABI pública não deve exigir índice explícito; -- `composer.emit_sprite(...)` deve receber o payload completo necessário para o frame: - - `glyph_id` - - `palette_id` - - `x` - - `y` - - `layer` - - `bank_id` - - `flip_x` - - `flip_y` - - `priority` -- a ABI pode futuramente agrupar esse payload se isso melhorar ergonomia, mas o contrato mínimo deve nascer completo; -- `active` não deve continuar no contrato canônico novo; -- overflow continua sendo ignorado com status/telemetria adequada, sem trapar o runtime. - -Para scene/camera, a recomendação provisória é: - -- manter o contrato mínimo já aceito internamente; -- `bind_scene` por bank id; -- `unbind_scene` explícito; -- `set_camera(x, y)` em pixel space com top-left viewport. -- `bind_scene(...)`, `unbind_scene(...)` e `emit_sprite(...)` devem usar `ComposerOpStatus` como retorno operacional canônico. - -## Perguntas em Aberto - -- Resolvido: - - o nome público canônico de sprite será `composer.emit_sprite(...)`; - - o syscall novo de sprite nasce completo com `glyph_id`, `palette_id`, `x`, `y`, `layer`, `bank_id`, `flip_x`, `flip_y`, `priority`; - - `gfx.set_sprite(...)` deve morrer e ser removido completamente; - - não haverá leitura de estado nesta primeira fase; - - `bind_scene(...)`, `unbind_scene(...)` e `emit_sprite(...)` usarão `ComposerOpStatus`; -- A ABI nova precisa expor refresh explícito, ou isso deve continuar totalmente interno ao `FrameComposer`? -- Resolvido: - - a ABI nova não deve expor refresh explícito; - - o domínio público canônico será `composer.*`, não `gfx.*`. - -## Criterio para Encerrar - -Esta agenda pode ser encerrada quando houver acordo explícito sobre: - -- a lista mínima de syscalls públicas canônicas do `FrameComposer`; -- o nome canônico da operação pública de sprite; -- a remoção completa de `gfx.set_sprite(...)` do contrato público; -- o formato de retorno/status das novas operações; -- a estratégia de transição necessária para decisão, plano e migração do restante da stack. - -## Resolucao em Andamento - -Direção atualmente acordada nesta agenda: - -- o namespace público canônico será `composer.*`; -- o núcleo mínimo inicial será: - - `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` -- não haverá introspecção pública nesta primeira fase; -- refresh/cache policy continua interno ao `FrameComposer`; -- `gfx.set_sprite(...)` não terá caminho de compatibilidade e deve ser removido. - -## Resolucao - -Esta agenda fica aceita com os seguintes pontos fechados: - -- o namespace público canônico do serviço será `composer.*`; -- a superfície mínima inicial será: - - `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` -- não haverá introspecção pública nesta primeira fase; -- não haverá refresh/cache policy público; -- `gfx.set_sprite(...)` deve ser removido completamente, sem shim de compatibilidade; -- a transição deve introduzir `composer.*` e remover `gfx.set_sprite(...)` na mesma thread de migração, com atualização coordenada de bytecode, cartridges, tests e runtime; -- a mesma thread deve atualizar a spec canônica do assunto e propagar a mudança para contratos de ABI e `ISA_CORE` quando essas superfícies forem impactadas pelo novo serviço público. diff --git a/discussion/workflow/agendas/AGD-0028-deferred-overlay-and-primitive-composition.md b/discussion/workflow/agendas/AGD-0028-deferred-overlay-and-primitive-composition.md deleted file mode 100644 index 677f31b7..00000000 --- a/discussion/workflow/agendas/AGD-0028-deferred-overlay-and-primitive-composition.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -id: AGD-0028 -ticket: deferred-overlay-and-primitive-composition -title: Deferred Overlay and Primitive Composition over FrameComposer -status: accepted -created: 2026-04-18 -updated: 2026-04-18 -resolved: 2026-04-18 -decision: DEC-0016 -tags: [gfx, runtime, render, frame-composer, overlay, primitives, hud] ---- - -## Contexto - -`FrameComposer.render_frame()` hoje recompõe o `back` no fim da logical frame. Quando há scene bound, o caminho `render_scene_from_cache(...)` limpa o buffer e desenha scene + sprites, o que apaga qualquer primitive ou `draw_text(...)` emitido antes via `gfx`. - -Isso expôs um conflito de modelo: - -- `composer.*` já é o caminho canônico de orquestração de frame; -- `gfx.draw_text(...)` e demais primitives ainda escrevem diretamente no `back`; -- o runtime só chama `render_frame()` no final do frame, então a escrita imediata em `back` deixou de ser semanticamente estável. -- As primitives de `gfx` não são o mecanismo desejado para composição de jogos com scene/tile/sprite; elas existem principalmente como debug, instrumentação visual e artefatos rápidos. - -Conteúdo relevante migrado de [AGD-0010](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md): - -- a arquitetura aceita continua sendo de framebuffer destrutivo em memória, não scene graph ou renderer tipo GPU; -- otimizações em primitives devem preservar a semântica observável, mesmo quando ganharem fast paths internos; -- existe preocupação explícita com custo por classe de primitive e com orçamento de memória no alvo handheld; -- caminhos de spans/linhas/clears são desejáveis como aceleração interna, mas sem reabrir o modelo operacional do pipeline do jogo. - -## Problema - -Precisamos decidir qual é o modelo canônico para primitives e texto no pipeline pós-`FrameComposer`. - -Sem isso: - -- texto e primitives continuam com comportamento dependente da ordem interna do renderer; -- o stress test e qualquer cartridge que combine `composer.*` com `gfx.*` terão resultado inconsistente; -- fica indefinido se primitives pertencem ao mundo, ao HUD, ou a um overlay final. - -## Pontos Criticos - -- `draw_text(...)` e primitives screen-space não podem depender de escrita imediata em `back`. -- Para esta thread, primitives de `gfx` devem permanecer agnósticas ao pipeline canônico de render do jogo e não devem ser mescladas semanticamente com tiles/sprites. -- A ordem de composição precisa ser explícita e estável: `scene -> sprites -> HUD -> primitives/debug overlay`, ou outra ordem formal equivalente. -- Precisamos decidir se o contrato público de `gfx.*` muda semanticamente sem mudar ABI, ou se parte dessa superfície migra para `composer.*`. -- A solução deve preservar o caminho sem scene bound. -- A implementação deve evitar contaminar a infraestrutura de `gfx` responsável por scene, sprites e HUD com estado misto de overlay/debug; se necessário, o overlay deve ter fila/fase própria. -- melhorias internas de primitive path devem continuar permitidas, desde que não mudem a semântica de overlay final e não exijam buffers extras incompatíveis com o orçamento de memória aceito. - -## Opcoes - -### Opcao 1 - Manter escrita direta em `back` - -- **Abordagem:** manter `gfx.draw_text(...)` e primitives rasterizando imediatamente. -- **Pro:** zero mudança estrutural agora. -- **Contra:** o modelo continua quebrado sempre que `render_frame()` recompõe o buffer depois. -- **Tradeoff:** só funciona de forma confiável fora do caminho canônico do `FrameComposer`. - -### Opcao 2 - Fila única de draw commands pós-scene/pós-sprite - -- **Abordagem:** transformar texto e primitives em comandos diferidos, drenados depois de `scene + sprites`. -- **Pro:** resolve o problema imediato de overlay/HUD e estabiliza o stress test. -- **Contra:** mistura HUD e primitives/debug sob o mesmo conceito, reduzindo clareza contratual mesmo quando a ordem prática for a mesma. -- **Tradeoff:** simples para V1, mas semanticamente mais fraco do que separar overlay de jogo e overlay de debug. - -### Opcao 3 - Separar HUD diferido de primitives/debug overlay final - -- **Abordagem:** tratar `gfx.draw_text(...)` e demais primitives de `gfx` como overlay/debug final, separado da composição canônica de jogo (`scene + sprites + HUD`). -- **Pro:** casa com a intenção declarada para `gfx.*`: debug, artefato rápido e instrumentação visual acima do frame do jogo. -- **Contra:** exige modelar explicitamente uma fase extra no pipeline. -- **Tradeoff:** aumenta a clareza contratual e evita mesclar primitives com o domínio de jogo. - -### Opcao 4 - Manter HUD e primitives no mesmo estágio final, mas com categorias separadas - -- **Abordagem:** drenar HUD e primitives ambos no fim do frame, porém com filas/categorias distintas e ordem formal `HUD -> primitives`. -- **Pro:** preserva implementação próxima entre caminhos similares, mantendo contrato separado. -- **Contra:** é mais custoso que a opção 3 sem entregar muito valor adicional imediato. -- **Tradeoff:** bom se já houver expectativa de HUD canônico separado no curtíssimo prazo. - -## Sugestao / Recomendacao - -Seguir com a **Opcao 3**. - -Minha recomendação é: - -- retirar a escrita direta em `back` como contrato operacional para `gfx.draw_text(...)` e demais primitives de `gfx`; -- introduzir uma fila diferida canônica de primitives/debug overlay drenada no fim do frame; -- tratar `gfx.*` primitive/text como superfície agnóstica ao pipeline de jogo e explicitamente acima da composição canônica; -- não misturar semanticamente primitives com scene/tile/sprite/HUD. -- evitar compartilhar indevidamente o mesmo mecanismo operacional de composição entre overlay/debug e os caminhos de scene/sprite/HUD, mesmo quando o backend de rasterização reutilizado for o mesmo. - -Ordem recomendada para o frame canônico: - -1. limpar/compor scene; -2. compor sprites; -3. compor HUD canônico, se existir; -4. aplicar `scene_fade`; -5. aplicar `hud_fade`; -6. drenar primitives/debug overlay de `gfx.*`. - -## Perguntas em Aberto - -- `draw_text(...)` e as demais primitives de `gfx` entram todas na mesma família de overlay final já na V1, ou começamos só com `draw_text(...)`? -- `render_no_scene_frame()` deve usar a mesma fila diferida para manter semântica idêntica com e sem scene? -- HUD canônico precisa existir explicitamente nesta mesma thread, ou pode continuar implícito/externo enquanto as primitives já migram para overlay final? -- quais fast paths internos de primitives continuam desejáveis nessa nova fase, por exemplo spans horizontais/verticais, fills e clears, sem misturar isso com a composição do jogo? -- o overlay/debug final precisa de dirtying próprio por classe de primitive ou isso pode ficar fora da primeira migração? - -## Criterio para Encerrar - -Esta agenda pode ser encerrada quando tivermos uma resposta explícita para: - -- o destino semântico de `draw_text(...)`; -- se haverá uma fila própria para primitives/debug overlay e qual a relação dela com HUD; -- a ordem canônica de composição do frame; -- o escopo exato da primeira migração implementável sem reabrir o restante do pipeline. - -## Resolucao Parcial - -Direção já aceita nesta agenda: - -- primitives e `draw_text(...)` de `gfx.*` devem ser tratadas como overlay/debug final; -- esse overlay deve ser drenado **depois** de `hud_fade`; -- scene, sprites e HUD canônico não devem ser semanticamente misturados com o overlay/debug; -- a implementação deve preservar separação operacional suficiente para que o `gfx` usado pelo pipeline do jogo não passe a depender do estado transitório de primitives/debug; -- otimizações de primitive path discutidas na `AGD-0010` continuam válidas, mas passam a operar dentro do domínio de overlay/debug final, não como parte da composição canônica de scene/sprite/HUD. - -## Resolucao - -Esta agenda fica aceita com os seguintes pontos fechados: - -- `gfx.draw_text(...)` e as demais primitives públicas de `gfx.*` pertencem à mesma família V1 de overlay/debug final; -- esse overlay/debug fica **fora** do `FrameComposer`; -- `FrameComposer` continua restrito à composição canônica do jogo (`scene`, `sprites` e HUD canônico quando existir); -- o overlay/debug deve ser drenado depois de `hud_fade`; -- o caminho sem scene bound deve observar a mesma semântica final de overlay/debug; -- HUD canônico explícito não faz parte desta thread e pode permanecer implícito/externo por enquanto; -- fast paths internos de primitives continuam permitidos, desde que preservem a semântica observável do overlay/debug final; -- dirtying granular ou otimizações finas por classe de primitive não fazem parte da primeira migração normativa desta thread. diff --git a/discussion/workflow/decisions/DEC-0014-frame-composer-render-integration.md b/discussion/workflow/decisions/DEC-0014-frame-composer-render-integration.md deleted file mode 100644 index db95ec61..00000000 --- a/discussion/workflow/decisions/DEC-0014-frame-composer-render-integration.md +++ /dev/null @@ -1,205 +0,0 @@ ---- -id: DEC-0014 -ticket: render-all-scene-cache-and-camera-integration -title: Frame Composer Render Integration -status: accepted -created: 2026-04-14 -accepted: 2026-04-14 -agenda: AGD-0026 -plans: [PLN-0017, PLN-0018, PLN-0019, PLN-0020, PLN-0021] -tags: [gfx, runtime, render, camera, scene, sprites] ---- - -## Status - -Accepted. - -## Contexto - -`DSC-0025` closed the canonical scene model around `SceneBank`, `SceneViewportCache`, and `SceneViewportResolver`, but the operational frame loop still remained split. `Gfx` still exposed `render_all()`, while the new world path already existed separately as `render_scene_from_cache(...)`. - -This left the runtime with an incomplete composition model: - -- canonical scene/camera/cache architecture had already changed; -- the normal frame entrypoint had not yet been integrated with that architecture; -- sprite ownership was still too coupled to `Gfx` and to a slot-first `active` model. - -This decision closes the ownership and composition model for the next integration phase. - -## Decisao - -The runtime SHALL converge to a `FrameComposer`-owned frame orchestration model. - -Normatively: - -- `Gfx.render_all()` MUST be retired as the canonical frame service. -- The canonical operational frame entrypoint SHALL become `FrameComposer.render_frame()`. -- `FrameComposer` SHALL live in `hardware/drivers`, alongside `Gfx`. -- `Hardware` SHALL aggregate both `FrameComposer` and `Gfx`. -- `FrameComposer` SHALL own the frame-operational state: - - active scene binding; - - camera / viewport state; - - `SceneViewportCache`; - - `SceneViewportResolver`; - - sprite submission state through `SpriteController`. -- `Gfx` SHALL remain a low-level visual backend responsible for composition, blit, and raster execution. -- `Gfx` MUST NOT remain the owner of scene state or sprite submission state. - -## Rationale - -This split preserves a clean ownership model: - -- `FrameComposer` decides what the frame is; -- `Gfx` executes how the frame is drawn. - -Keeping orchestration in `FrameComposer` avoids re-entangling renderer code with camera policy, cache refresh policy, and scene binding. Keeping `FrameComposer` in `hardware/drivers` instead of `hal` preserves room for backend-specific acceleration while avoiding a policy-heavy abstraction in HAL. - -This also preserves future backend freedom: - -- software path today; -- hardware-assisted blit path later; -- or a more PPU-like backend in bare-metal environments. - -It also preserves the scene-model contract already accepted below the frame layer: - -- tile size is a property of each scene layer; -- the frame orchestrator must consume that contract, not redefine it; -- `FrameComposer` must therefore remain tile-size agnostic rather than hard-coding `16x16` assumptions. - -## Invariantes / Contrato - -### 1. Frame Entry - -- The canonical public frame orchestration path SHALL be `FrameComposer.render_frame()`. -- `FrameComposer.render_frame()` SHALL be capable of producing a valid frame 100% of the time. -- A valid frame MUST NOT require a scene to be bound. - -### 2. No-Scene Behavior - -- If no scene is bound, `FrameComposer.render_frame()` SHALL compose only: - - emitted sprites; - - fades already owned by the visual backend. -- No implicit clear SHALL be performed. -- Clearing the back buffer SHALL remain the responsibility of the caller / developer. -- In the no-scene state: - - cache MUST be absent or inert; - - resolver MUST be absent or inert; - - the system SHALL expose explicit scene-availability status. - -### 3. Scene Binding - -- Scene binding SHALL be performed by `bind_scene(scene_bank_id)`. -- `FrameComposer` SHALL depend on `SceneBankPoolAccess`. -- `FrameComposer` MUST resolve scenes through the pool, not through copied scene values. -- Scene access MUST be pointer-based / shared-reference based only. -- On bind, `FrameComposer` SHALL store: - - `scene_bank_id`; - - `Arc` for the resolved scene. -- The `SceneViewportCache` SHALL live inside `FrameComposer` while the scene remains bound. -- `unbind_scene()` SHALL: - - remove the active scene; - - discard the associated cache; - - invalidate the world path; - - keep the frame path valid for no-scene composition. -- Replacing the contents of a bound scene slot SHALL require a new explicit bind. -- `FrameComposer` MUST NOT poll the scene bank pool each frame to revalidate the binding. -- A new `bind_scene(...)` SHALL replace the previous bound scene completely. - -### 4. Camera - -- The V1 camera contract SHALL be minimal. -- `set_camera(x, y)` SHALL accept `i32` pixel coordinates. -- `x` and `y` SHALL represent the top-left of the viewport in world space. -- Camera follow, smoothing, shake, cinematic transitions, and similar behaviors are OUT OF SCOPE for this decision. - -### 5. Cache and Resolver - -- `FrameComposer` SHALL own both `SceneViewportCache` and `SceneViewportResolver`. -- `FrameComposer` SHALL apply `CacheRefreshRequest`s to the cache. -- `Gfx` MUST NOT own cache refresh policy. -- `Gfx` MUST only consume already prepared render state. - -### 5A. Tile Size Contract - -- `FrameComposer` SHALL remain tile-size agnostic. -- `FrameComposer` MUST accept scene layers whose canonical `tile_size` is `8x8`, `16x16`, or `32x32`. -- `FrameComposer` MUST NOT impose `16x16` as a bind-time, cache-time, resolver-time, or render-time precondition. -- Cache sizing, resolver math, and world-copy preparation SHALL derive from the `tile_size` declared by each bound scene layer. -- Compatibility checks, when needed, MUST be derived from canonical scene-layer and glyph-bank metadata rather than from a hard-coded compositor default. -- Any implementation path that only works for `16x16` tiles is NON-COMPLIANT with this decision. - -### 6. Sprite Model - -- `Sprite.active` MUST be removed from the canonical operational model. -- Sprite submission SHALL become frame-emission based. -- `SpriteController` SHALL be the sprite submission subsystem owned by `FrameComposer`. -- The sprite frame capacity SHALL remain capped at `512` for V1. -- The sprite counter SHALL be reset at the start of each frame. -- The caller MUST NOT provide sprite indices directly. -- Each `emit_sprite(...)` call SHALL occupy the next available internal slot. -- Overflow beyond capacity SHALL be ignored. -- Overflow SHOULD leave room for system logging / telemetry. -- Future certification MAY penalize sprite overflow, but that is not part of this decision. -- `emit_sprite(...)` SHALL NOT require a dedicated reset API beyond the normal frame lifecycle. - -### 7. Sprite Ordering - -- Each sprite SHALL carry: - - `layer`; - - `priority`. -- `layer` SHALL remain numeric for now. -- The sprite `layer` type SHALL match the scene layer reference type used by the scene model. -- Composition SHALL be layer-based. -- Within a layer: - - lower `priority` SHALL render first; - - ties SHALL resolve FIFO by emission order. - -### 8. Composition Scope - -- HUD integration is OUT OF SCOPE for the first integration phase covered by this decision. -- The first integration phase SHALL focus on: - - world scene path; - - sprites; - - fades. - -## Impactos - -### HAL - -- `GfxBridge` and adjacent visual contracts will need to stop treating `render_all()` as the canonical operational frame path. - -### Drivers / Hardware - -- `Hardware` will need to aggregate `FrameComposer` next to `Gfx`. -- `Gfx` will need to lose ownership of scene/sprite operational state. -- Sprite submission state will need to move into `SpriteController`. -- `FrameComposer`, cache, and resolver integration must preserve per-layer `tile_size` semantics, including `8x8`. - -### Runtime / VM - -- The VM runtime will eventually trigger frame composition through the new `FrameComposer` path rather than depending on `Gfx.render_all()`. -- The VM/runtime side should not own the detailed cache or scene orchestration policy directly once `FrameComposer` exists in hardware/drivers. - -### Asset / Scene Flow - -- Scene activation will become explicit through bank-id binding. -- Scene slot replacement will require explicit rebinding behavior from callers. -- Scene-driven tile-size metadata must propagate unchanged into `FrameComposer` orchestration and backend copy preparation. - -## Referencias - -- [AGD-0026-render-all-scene-cache-and-camera-integration.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md) -- [LSN-0030-canonical-scene-cache-and-resolver-split.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/lessons/DSC-0025-scene-bank-and-viewport-cache-refactor/LSN-0030-canonical-scene-cache-and-resolver-split.md) - -## Propagacao Necessaria - -- A new implementation plan MUST be created from this decision before code changes. -- `FrameComposer` and `SpriteController` need explicit planning and migration sequencing. -- `Gfx.render_all()` retirement MUST be planned rather than removed ad hoc. -- The frame service rename and integration path MUST be propagated through the frame loop callsites. -- Plan steps and tests that cover world composition MUST explicitly include `8x8` tile-size coverage. - -## Revision Log - -- 2026-04-14: Initial accepted decision from `AGD-0026`. -- 2026-04-15: Revision accepted to make `FrameComposer` explicitly tile-size agnostic and to require `8x8` support alongside `16x16` and `32x32`. diff --git a/discussion/workflow/decisions/DEC-0015-frame-composer-public-syscall-surface.md b/discussion/workflow/decisions/DEC-0015-frame-composer-public-syscall-surface.md deleted file mode 100644 index f0fb8fba..00000000 --- a/discussion/workflow/decisions/DEC-0015-frame-composer-public-syscall-surface.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -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`. diff --git a/discussion/workflow/decisions/DEC-0016-deferred-gfx-overlay-outside-frame-composer.md b/discussion/workflow/decisions/DEC-0016-deferred-gfx-overlay-outside-frame-composer.md deleted file mode 100644 index be739372..00000000 --- a/discussion/workflow/decisions/DEC-0016-deferred-gfx-overlay-outside-frame-composer.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -id: DEC-0016 -ticket: deferred-overlay-and-primitive-composition -title: Deferred GFX Overlay Outside FrameComposer -status: accepted -created: 2026-04-18 -accepted: 2026-04-18 -agenda: AGD-0028 -plans: [PLN-0026, PLN-0027, PLN-0028, PLN-0029] -tags: [gfx, runtime, render, frame-composer, overlay, primitives, hud] ---- - -## Status - -Accepted. - -## Contexto - -`DEC-0014` and `DEC-0015` established `FrameComposer` as the canonical orchestration path for game-frame composition and exposed that orchestration publicly through `composer.*`. - -That migration left `gfx.draw_text(...)` and other `gfx` primitives with their historical immediate-write behavior against the working framebuffer. Once the runtime moved to end-of-frame composition through `FrameComposer.render_frame()`, those immediate writes became unstable: scene-backed frame composition can rebuild the backbuffer after primitive calls have already touched it. - -The resulting conflict is not about whether primitives should remain available. It is about their semantic place in the pipeline. The accepted direction of this thread is that `gfx` primitives are not part of the canonical game composition model. They are primarily for debug, quick visual instrumentation, and rapid artifacts, and they must remain agnostic to scene/tile/sprite/HUD composition. - -Relevant performance context migrated from `AGD-0010` also remains in force: - -- the renderer continues to be a destructive software framebuffer model, not a retained scene graph or GPU-style renderer; -- internal primitive fast paths remain desirable; -- memory growth must remain constrained for the handheld target; -- optimization of primitive execution must not alter observable semantics. - -## Decisao - -`gfx.*` primitives and text SHALL move to a deferred final overlay model that lives outside `FrameComposer`. - -Normatively: - -- `FrameComposer` SHALL remain responsible only for canonical game-frame composition: - - scene composition; - - sprite composition; - - canonical HUD composition when such a HUD stage exists. -- `FrameComposer` MUST NOT become the owner of debug/primitive overlay state. -- Public `gfx.*` primitives, including `gfx.draw_text(...)`, SHALL belong to a V1 `gfx` overlay/debug family. -- That overlay/debug family SHALL be deferred rather than written immediately as the stable operational contract. -- The deferred overlay/debug stage SHALL be drained after `hud_fade`. -- The deferred overlay/debug stage SHALL be above scene, sprites, and canonical HUD in final visual order. -- The no-scene path MUST preserve the same final overlay/debug semantics. -- `gfx.*` primitives MUST remain semantically separate from scene/tile/sprite/HUD composition. -- The implementation MUST preserve operational separation sufficient to prevent the canonical game pipeline from depending on transient primitive/debug state. - -## Rationale - -This decision keeps the architectural boundary clean. - -`FrameComposer` exists to own the canonical game frame. Debug primitives do not belong to that contract. Pulling them into `FrameComposer` would make the orchestration service responsible for a second semantic domain with different goals: - -- game composition must be deterministic and canonical; -- primitive/text overlay must be opportunistic, screen-space, and pipeline-agnostic. - -Keeping overlay/debug outside `FrameComposer` also aligns with the stated product intent: these primitives are useful helpers, but they are not meant to become a second composition language for games. - -Draining them after `hud_fade` preserves the user-visible requirement that debug/overlay content stay truly on top and legible. This is more faithful to the accepted intent than treating primitives as part of HUD or world composition. - -Finally, separating semantic ownership still leaves room for implementation reuse. Raster backends, span paths, and buffer-writing helpers may still be shared internally, provided the public operational model remains separate. - -## Invariantes / Contrato - -### 1. Ownership Boundary - -- `FrameComposer` MUST own only canonical game-frame composition. -- Primitive/debug overlay state MUST live outside `FrameComposer`. -- The canonical game pipeline MUST NOT depend on primitive/debug overlay state for correctness. - -### 2. Overlay Semantics - -- `gfx.draw_text(...)` and sibling `gfx` primitives SHALL be treated as deferred final overlay/debug operations. -- Immediate direct writes to `back` MUST NOT remain the stable operational contract for these primitives. -- Final overlay/debug output MUST appear after: - - scene composition; - - sprite composition; - - canonical HUD composition, if present; - - `scene_fade`; - - `hud_fade`. - -### 3. Separation from Game Composition - -- Primitive/debug overlay MUST NOT be reinterpreted as scene content. -- Primitive/debug overlay MUST NOT be reinterpreted as sprite content. -- Primitive/debug overlay MUST NOT be the vehicle for canonical HUD composition. -- The public `gfx.*` primitive surface SHALL remain pipeline-agnostic relative to `composer.*`. - -### 4. Consistency Across Frame Paths - -- The scene-bound path and no-scene path MUST expose the same final overlay/debug behavior. -- Users MUST NOT need to know whether a scene is bound for `gfx.*` primitives to appear as final overlay/debug content. - -### 5. Internal Optimization Contract - -- Internal fast paths for lines, spans, fills, clears, or similar primitive operations MAY be introduced. -- Such fast paths MUST preserve the observable deferred overlay/debug semantics. -- This decision DOES NOT require fine-grained dirtying or per-primitive-class invalidation in the first migration. - -## Impactos - -### Runtime / Drivers - -- The runtime frame-end sequence must gain a distinct overlay/debug drain stage outside `FrameComposer`. -- `gfx.draw_text(...)` and peer primitives can no longer rely on stable immediate framebuffer writes once this migration lands. - -### GFX Backend - -- `Gfx` will need an explicit deferred overlay/debug command path or equivalent subsystem boundary. -- Shared raster helpers remain allowed, but the overlay/debug phase must stay semantically distinct from scene/sprite/HUD composition. - -### FrameComposer - -- `FrameComposer` must remain free of primitive/debug overlay ownership. -- Any future HUD integration must not collapse that boundary. - -### Spec / Docs - -- The canonical graphics/runtime spec must describe `gfx.*` primitives as deferred final overlay/debug operations rather than stable immediate backbuffer writes. -- Documentation that describes frame ordering must show overlay/debug after `hud_fade`. - -### Performance Follow-up - -- `AGD-0010` remains the home for broader renderer performance work, dirtying strategy, and low-level primitive optimization policy. -- Primitive optimization carried out under that thread must respect the normative separation established here. - -## Referencias - -- [AGD-0028-deferred-overlay-and-primitive-composition.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/agendas/AGD-0028-deferred-overlay-and-primitive-composition.md) -- [AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.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) -- [DEC-0015-frame-composer-public-syscall-surface.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/decisions/DEC-0015-frame-composer-public-syscall-surface.md) - -## Propagacao Necessaria - -- A new implementation plan MUST be created before code changes. -- That plan MUST cover: - - deferred overlay/debug ownership outside `FrameComposer`; - - runtime frame-end ordering changes; - - no-scene path parity; - - spec/documentation updates for `gfx.*` primitive semantics. -- The implementation plan MUST NOT reopen the ownership boundary accepted here. - -## Revision Log - -- 2026-04-18: Initial accepted decision from `AGD-0028`. -- 2026-04-18: Linked implementation plan family `PLN-0026` through `PLN-0029`. diff --git a/discussion/workflow/plans/PLN-0017-frame-composer-core-and-hardware-ownership.md b/discussion/workflow/plans/PLN-0017-frame-composer-core-and-hardware-ownership.md deleted file mode 100644 index e7d3ff62..00000000 --- a/discussion/workflow/plans/PLN-0017-frame-composer-core-and-hardware-ownership.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -id: PLN-0017 -ticket: render-all-scene-cache-and-camera-integration -title: Plan - FrameComposer Core and Hardware Ownership -status: accepted -created: 2026-04-14 -completed: -tags: [gfx, runtime, render, hardware, frame-composer] ---- - -## Objective - -Introduce `FrameComposer` as a first-class hardware-side subsystem and move canonical frame orchestration ownership out of `Gfx`. - -## Background - -`DEC-0014` locks `FrameComposer` as the canonical frame orchestration service. The first implementation step is to create the owning type, place it in `hardware/drivers`, and make `Hardware` aggregate it next to `Gfx` without yet completing the full render-path migration. - -## Scope - -### Included -- Create the `FrameComposer` type in `crates/console/prometeu-drivers`. -- Define the minimal owned state shape: - - active scene binding state; - - camera / viewport state; - - optional cache; - - optional resolver; - - owned `SpriteController`. -- Preserve scene-layer metadata naming aligned with the world path contract, including `parallax_factor` as the canonical per-layer camera multiplier field. -- Aggregate `FrameComposer` inside `Hardware`. -- Expose the minimum driver-facing surface required for subsequent plans. - -### Excluded -- full sprite-model migration -- full scene binding implementation -- cache refresh application -- render-path retirement of `Gfx.render_all()` - -## Execution Steps - -### Step 1 - Introduce the `FrameComposer` module and owned state - -**What:** -Create `FrameComposer` as a concrete driver-side subsystem. - -**How:** -- Add a new module such as `crates/console/prometeu-drivers/src/frame_composer.rs`. -- Define a `FrameComposer` struct with explicit placeholders for: - - `active_scene_id` - - `active_scene` - - `scene_status` - - `camera_x_px` - - `camera_y_px` - - `SceneViewportCache` - - `SceneViewportResolver` - - `SpriteController` -- Keep scene/cache/resolver fields optional where no-scene operation is required. -- Do not introduce any fixed `16x16` assumption into owned state; tile-size-sensitive behavior must derive from bound scene metadata. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` -- `crates/console/prometeu-drivers/src/lib.rs` - -### Step 2 - Aggregate `FrameComposer` in `Hardware` - -**What:** -Make `Hardware` own `FrameComposer` alongside `Gfx`. - -**How:** -- Extend `Hardware` with a `frame_composer` field. -- Wire construction so `FrameComposer` receives the shared bank access it needs for later plans. -- Keep ownership boundaries explicit: `FrameComposer` prepares frame state, `Gfx` remains backend. - -**File(s):** -- `crates/console/prometeu-drivers/src/hardware.rs` -- `crates/console/prometeu-drivers/src/memory_banks.rs` - -### Step 3 - Define the minimum public driver-facing surface - -**What:** -Give the driver layer a stable initial surface for `FrameComposer`. - -**How:** -- Expose minimal constructor and accessor paths. -- Do not yet overdesign HAL-facing traits. -- Ensure the code compiles with no implicit dependence on `Gfx.render_all()` ownership for frame state. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` -- `crates/console/prometeu-drivers/src/hardware.rs` -- `crates/console/prometeu-drivers/src/lib.rs` - -## Test Requirements - -### Unit Tests -- `FrameComposer` can be constructed without a bound scene. -- `Hardware` successfully constructs with both `gfx` and `frame_composer`. -- Construction and owned state shape do not encode `16x16` as an implicit world-path invariant. - -### Integration Tests -- Shared bank access needed by `FrameComposer` is available through hardware construction. - -### Manual Verification -- Inspect the resulting type ownership and confirm scene/sprite state is no longer being newly introduced into `Gfx`. - -## Acceptance Criteria - -- [ ] `FrameComposer` exists as a dedicated driver-side subsystem. -- [ ] `Hardware` aggregates `FrameComposer` next to `Gfx`. -- [ ] `FrameComposer` has explicit owned placeholders for scene/camera/cache/resolver/sprites. -- [ ] The build remains green with the new ownership structure in place. - -## Dependencies - -- Source decision: `DEC-0014` - -## Risks - -- Introducing `FrameComposer` with too much behavior too early can blur later migration steps. -- Introducing too little owned state can leave ownership ambiguous and force rework in later plans. -- Encoding `16x16` into the initial owned-state shape would create a contract violation that later plans would have to unwind. diff --git a/discussion/workflow/plans/PLN-0018-sprite-controller-and-frame-emission-model.md b/discussion/workflow/plans/PLN-0018-sprite-controller-and-frame-emission-model.md deleted file mode 100644 index c101524e..00000000 --- a/discussion/workflow/plans/PLN-0018-sprite-controller-and-frame-emission-model.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -id: PLN-0018 -ticket: render-all-scene-cache-and-camera-integration -title: Plan - SpriteController and Frame Emission Model -status: accepted -created: 2026-04-14 -completed: -tags: [gfx, runtime, render, sprites, frame-composer] ---- - -## Objective - -Replace the slot-first sprite model with a `FrameComposer`-owned `SpriteController` that emits sprites per frame instead of relying on `Sprite.active` and caller-provided indices. - -## Background - -`DEC-0014` removes `Sprite.active` from the canonical operational model and locks sprite submission to a frame-emission model owned by `SpriteController`. - -## Scope - -### Included -- Introduce `SpriteController`. -- Remove the operational dependence on `Sprite.active`. -- Remove caller-owned sprite indices from the canonical submission path. -- Add layer + priority ordering with FIFO tie-breaking. -- Preserve the capacity cap of `512` sprites per frame. - -### Excluded -- HUD integration -- scene binding -- cache refresh logic - -## Execution Steps - -### Step 1 - Redefine the sprite operational model - -**What:** -Move canonical sprite submission semantics from slot-first to frame-emission. - -**How:** -- Update `Sprite` and adjacent APIs so the canonical path no longer depends on `active`. -- Keep layer numeric and aligned with the scene layer reference type. -- Preserve `priority` as the within-layer ordering field. - -**File(s):** -- `crates/console/prometeu-hal/src/sprite.rs` -- any adjacent driver-side sprite helpers - -### Step 2 - Implement `SpriteController` - -**What:** -Create the owned sprite subsystem under `FrameComposer`. - -**How:** -- Add a `SpriteController` type with: - - storage capacity `512` - - frame counter - - per-layer buckets - - stable FIFO semantics for equal priority -- Add `begin_frame()` behavior that clears counters and buckets. -- Add `emit_sprite(...)` behavior that appends to the next internal slot. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` -- optional dedicated `sprite_controller.rs` - -### Step 3 - Handle overflow and logging semantics - -**What:** -Implement overflow behavior without turning it into a hard runtime failure. - -**How:** -- Ignore sprites emitted after capacity is reached. -- Leave explicit room for system logging / telemetry. -- Do not add a special reset API beyond the normal frame lifecycle. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` -- related telemetry/log hooks if needed - -### Step 4 - Remove stale slot-first sprite entrypoints - -**What:** -Retire the old “set sprite by explicit index” path from the canonical model. - -**How:** -- Identify the current caller-facing `Gfx` sprite mutation surface. -- Migrate it toward `FrameComposer`-owned submission. -- Keep transitional shims only if required to preserve buildability for the next plan. - -**File(s):** -- `crates/console/prometeu-drivers/src/gfx.rs` -- `crates/console/prometeu-hal/src/gfx_bridge.rs` -- `crates/console/prometeu-drivers/src/frame_composer.rs` - -## Test Requirements - -### Unit Tests -- `begin_frame()` resets sprite count and buckets. -- `emit_sprite(...)` appends without caller-provided index. -- lower `priority` renders first within a layer. -- equal `priority` resolves FIFO by registration order. -- overflow drops excess sprites without panicking. - -### Integration Tests -- `FrameComposer` can emit sprites and provide ordered sprite state for rendering. - -### Manual Verification -- Confirm no canonical submission path requires `Sprite.active` or explicit slot index anymore. - -## Acceptance Criteria - -- [ ] `SpriteController` exists under `FrameComposer`. -- [ ] `Sprite.active` is no longer required by the canonical frame path. -- [ ] Caller-provided sprite indices are retired from the canonical submission path. -- [ ] Layer/priority/FIFO ordering is implemented and tested. -- [ ] Overflow is ignored with space left for logging. - -## Dependencies - -- Depends on `PLN-0017` -- Source decision: `DEC-0014` - -## Risks - -- Keeping compatibility shims too long can leave the codebase in a dual sprite model. -- Removing index-based APIs too early may break callsites before `FrameComposer` integration is ready. diff --git a/discussion/workflow/plans/PLN-0019-scene-binding-camera-and-scene-status.md b/discussion/workflow/plans/PLN-0019-scene-binding-camera-and-scene-status.md deleted file mode 100644 index 1b83928d..00000000 --- a/discussion/workflow/plans/PLN-0019-scene-binding-camera-and-scene-status.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -id: PLN-0019 -ticket: render-all-scene-cache-and-camera-integration -title: Plan - Scene Binding, Camera, and Scene Status -status: accepted -created: 2026-04-14 -completed: -tags: [gfx, runtime, render, scene, camera, frame-composer] ---- - -## Objective - -Implement the `FrameComposer` scene-binding contract, minimal camera state, and explicit scene-availability status without yet completing the cache-refresh render path. - -## Background - -`DEC-0014` locks scene activation around `bind_scene(scene_bank_id)` with `SceneBankPoolAccess`, pointer-based access only, and `scene_bank_id + Arc` retained inside `FrameComposer`. -The same decision also requires `FrameComposer` to remain tile-size agnostic and to preserve canonical per-layer `tile_size`, including `8x8`. -For the scene-layer motion contract, this plan treats `parallax_factor` as the canonical field name for the per-layer camera multiplier. - -## Scope - -### Included -- scene bind/unbind contract -- active scene identity and shared reference storage -- scene availability status -- minimal camera state (`i32`, top-left viewport) - -### Excluded -- applying cache refreshes -- full render-path migration -- HUD behavior - -## Execution Steps - -### Step 1 - Add scene binding state to `FrameComposer` - -**What:** -Implement the canonical bind/unbind surface. - -**How:** -- Add `bind_scene(scene_bank_id)` and `unbind_scene()`. -- Resolve scenes from `SceneBankPoolAccess`. -- Store both: - - `scene_bank_id` - - `Arc` -- Replace prior scene binding completely on a new bind. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` -- `crates/console/prometeu-drivers/src/memory_banks.rs` - -### Step 2 - Add explicit scene status - -**What:** -Expose scene availability through status, not just implicit option checks. - -**How:** -- Define a scene status enum or equivalent status object. -- Distinguish at least: - - no scene bound - - bound and available - - bound but not renderable if such intermediate state is needed -- Ensure no-scene rendering remains valid. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` -- optional HAL-facing status surface if needed later - -### Step 3 - Add camera contract - -**What:** -Implement the V1 camera ownership inside `FrameComposer`. - -**How:** -- Add `set_camera(x, y)`. -- Store camera coordinates as `i32`. -- Treat them as top-left viewport coordinates in world space. -- Keep all advanced camera behavior out of scope. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` - -### Step 4 - Tie cache/resolver lifetime to scene binding - -**What:** -Align cache/resolver lifetime with the active scene contract. - -**How:** -- Cache and resolver remain `None` / absent when no scene is bound. -- On bind: - - create or reinitialize cache/resolver. -- On unbind: - - discard cache/resolver and invalidate the world path. -- Any initialization must derive layer math from the bound scene tile sizes instead of assuming `16x16`. -- Any layer-camera math or related contract references must use `parallax_factor` terminology rather than generic `motion` naming. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` - -## Test Requirements - -### Unit Tests -- bind stores `scene_bank_id + Arc`. -- unbind clears active scene and cache. -- scene status reflects no-scene and active-scene states. -- camera coordinates are stored as top-left pixel-space values. -- bind/unbind remains valid for scenes whose layers use `8x8` tiles. -- scene binding and camera-facing contracts preserve `parallax_factor` as the canonical layer field name. - -### Integration Tests -- `FrameComposer` can resolve a scene from the pool and survive no-scene operation. - -### Manual Verification -- Confirm scene access remains pointer-based and no scene copies are introduced. - -## Acceptance Criteria - -- [ ] `FrameComposer` binds scenes by bank id through `SceneBankPoolAccess`. -- [ ] Active binding stores both scene id and shared scene reference. -- [ ] Scene status is explicit. -- [ ] Camera contract is implemented as `i32` top-left viewport coordinates. -- [ ] Cache/resolver lifetime follows scene bind/unbind. -- [ ] Scene bind/cache/resolver setup preserves canonical per-layer tile sizes, including `8x8`. -- [ ] Scene-layer camera multiplier naming is aligned on `parallax_factor`. - -## Dependencies - -- Depends on `PLN-0017` -- Source decision: `DEC-0014` - -## Risks - -- Weak scene-status semantics can make no-scene behavior ambiguous in later render integration. -- If cache/resolver lifetime is not tied cleanly to binding, stale world state can leak across scene transitions. diff --git a/discussion/workflow/plans/PLN-0020-cache-refresh-and-render-frame-path.md b/discussion/workflow/plans/PLN-0020-cache-refresh-and-render-frame-path.md deleted file mode 100644 index 925517e8..00000000 --- a/discussion/workflow/plans/PLN-0020-cache-refresh-and-render-frame-path.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -id: PLN-0020 -ticket: render-all-scene-cache-and-camera-integration -title: Plan - Cache Refresh and render_frame Path -status: accepted -created: 2026-04-14 -completed: -tags: [gfx, runtime, render, cache, resolver, frame-composer] ---- - -## Objective - -Connect `FrameComposer` to `SceneViewportResolver`, apply cache refreshes inside `FrameComposer`, and establish `render_frame()` as the canonical composition path for world + sprites + fades. - -## Background - -`DEC-0014` requires that cache refresh policy remain inside `FrameComposer` and that `FrameComposer.render_frame()` become the canonical frame entry while `Gfx` remains only the low-level execution backend. -`DEC-0014` also requires the world path to remain tile-size agnostic, with explicit support for `8x8`, `16x16`, and `32x32` scene-layer tile sizes. -For per-layer camera scaling, this plan treats `parallax_factor` as the canonical scene-layer field name. - -## Scope - -### Included -- apply `CacheRefreshRequest`s in `FrameComposer` -- connect camera/scene state to resolver updates -- use cache-backed world rendering in the frame path -- keep valid no-scene rendering (`sprites + fades`) - -### Excluded -- HUD integration -- final retirement cleanup of legacy callsites - -## Execution Steps - -### Step 1 - Apply resolver refreshes inside `FrameComposer` - -**What:** -Move cache-refresh orchestration fully into `FrameComposer`. - -**How:** -- On active-scene frames: - - call resolver update with current camera and scene - - consume returned `CacheRefreshRequest`s - - apply them to `SceneViewportCache` -- Keep `Gfx` unaware of refresh semantics. -- Ensure resolver and refresh math follow the bound layer `tile_size` values rather than any fixed `16x16` default. -- Ensure per-layer camera math is expressed through `parallax_factor` naming in the resolver/cache path. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` - -### Step 2 - Define `render_frame()` as the canonical frame path - -**What:** -Introduce the new frame service on `FrameComposer`. - -**How:** -- Add `render_frame()` to `FrameComposer`. -- If a scene is active and renderable: - - prepare resolver update - - refresh cache - - call the cache-backed world path in `Gfx` -- If no scene is active: - - call the no-scene path for `sprites + fades` -- World rendering must remain valid when the active scene uses `8x8` tiles. - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` -- `crates/console/prometeu-drivers/src/gfx.rs` - -### Step 3 - Keep `Gfx` as backend only - -**What:** -Narrow `Gfx` to backend-oriented composition responsibilities. - -**How:** -- Ensure `Gfx` consumes prepared state from `FrameComposer`. -- Do not let `Gfx` regain ownership of cache refresh or scene orchestration. -- Keep low-level helpers for cache-backed copy paths, sprite drawing, and fades in `Gfx`. - -**File(s):** -- `crates/console/prometeu-drivers/src/gfx.rs` - -### Step 4 - Cover scene and no-scene frame paths - -**What:** -Protect the two canonical frame modes. - -**How:** -- Add tests for: - - active-scene world composition - - no-scene `sprites + fades` - - scene transition through unbind/rebind - - cache refresh behavior staying inside `FrameComposer` - - active-scene composition with `8x8` tile-size layers - -**File(s):** -- `crates/console/prometeu-drivers/src/frame_composer.rs` -- `crates/console/prometeu-drivers/src/gfx.rs` - -## Test Requirements - -### Unit Tests -- `render_frame()` with no scene produces valid no-scene composition. -- `render_frame()` with a scene applies resolver refreshes before composition. -- cache refresh requests are applied by `FrameComposer`, not `Gfx`. -- `render_frame()` with an `8x8` scene uses resolver/cache math derived from layer tile size rather than a `16x16` assumption. -- Resolver/cache-facing tests use `parallax_factor` terminology for per-layer camera scaling. - -### Integration Tests -- scene bind + camera set + sprite emission + `render_frame()` produces the expected composed frame. -- scene bind + camera set + `8x8` scene + `render_frame()` produces the expected composed frame. - -### Manual Verification -- Verify that no-scene frames still render sprites/fades without crashes or hidden clears. - -## Acceptance Criteria - -- [ ] `FrameComposer.render_frame()` exists and is the canonical frame path. -- [ ] Cache refreshes are applied inside `FrameComposer`. -- [ ] World rendering consumes the cache-backed path. -- [ ] No-scene `sprites + fades` behavior remains valid. -- [ ] `Gfx` remains backend-only for this path. -- [ ] The world path is explicitly covered for `8x8` scenes without `16x16`-specific assumptions. -- [ ] Resolver/cache/frame-path terminology is aligned on `parallax_factor` for scene-layer camera scaling. - -## Dependencies - -- Depends on `PLN-0017`, `PLN-0018`, and `PLN-0019` -- Source decision: `DEC-0014` - -## Risks - -- If refresh application leaks into `Gfx`, the ownership split from `DEC-0014` collapses. -- If no-scene behavior is not tested explicitly, scene integration can accidentally make scene binding mandatory. -- If tests cover only `16x16`, a latent compositor regression against canonical `8x8` scenes can ship unnoticed. diff --git a/discussion/workflow/plans/PLN-0021-service-retirement-callsite-migration-and-regression.md b/discussion/workflow/plans/PLN-0021-service-retirement-callsite-migration-and-regression.md deleted file mode 100644 index 00edec77..00000000 --- a/discussion/workflow/plans/PLN-0021-service-retirement-callsite-migration-and-regression.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -id: PLN-0021 -ticket: render-all-scene-cache-and-camera-integration -title: Plan - Service Retirement, Callsite Migration, and Regression Coverage -status: accepted -created: 2026-04-14 -completed: -tags: [gfx, runtime, render, migration, regression] ---- - -## Objective - -Retire `Gfx.render_all()` from the canonical flow, migrate callsites to `FrameComposer.render_frame()`, and add the regression coverage needed to lock the new service model. - -## Background - -`DEC-0014` is explicit that `Gfx.render_all()` must be retired and that `FrameComposer.render_frame()` becomes the canonical frame orchestration entrypoint. This final plan removes the old canonical service shape and validates the migration end-to-end. -The same decision also requires the new canonical path to preserve scene-layer tile sizes such as `8x8`, not just `16x16`. - -## Scope - -### Included -- retire `Gfx.render_all()` from the canonical path -- migrate frame-loop callsites -- align bridge surfaces as needed -- add regression coverage for the final service model - -### Excluded -- HUD integration -- future certification behavior for sprite overflow - -## Execution Steps - -### Step 1 - Migrate frame-loop callsites - -**What:** -Switch runtime frame execution from `Gfx.render_all()` to `FrameComposer.render_frame()`. - -**How:** -- Identify all canonical callsites that currently trigger `Gfx.render_all()`. -- Update them to go through `FrameComposer`. -- Preserve present/swap behavior after the render call. - -**File(s):** -- `crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs` -- any additional runtime frame-loop callsites - -### Step 2 - Retire `Gfx.render_all()` from the canonical service surface - -**What:** -Remove the old frame service as the operational entry. - -**How:** -- Remove or deprecate `render_all()` from `Gfx` and `GfxBridge` as the canonical render entry. -- Keep only backend-oriented helpers that `FrameComposer` calls. -- Ensure the naming and public path converge to Rust-style `render_frame()`. - -**File(s):** -- `crates/console/prometeu-hal/src/gfx_bridge.rs` -- `crates/console/prometeu-drivers/src/gfx.rs` - -### Step 3 - Add end-to-end regression coverage - -**What:** -Protect the new service model against fallback to the old renderer path. - -**How:** -- Add tests that prove: - - frame-loop code calls `FrameComposer.render_frame()` - - no-scene frames remain valid - - active-scene frames render through cache-backed composition - - active-scene frames remain valid for canonical `8x8` scenes - - sprite emission and ordering survive the full path -- Add assertions or test failures for accidental continued reliance on `Gfx.render_all()`. - -**File(s):** -- runtime tests -- driver tests -- bridge tests where needed - -### Step 4 - Validate full repository behavior - -**What:** -Confirm the migration did not break unrelated systems. - -**How:** -- Run the repository validation command required by current practice. -- Keep regression evidence attached to the plan execution. - -**File(s):** -- repository-wide CI / validation entrypoints - -## Test Requirements - -### Unit Tests -- `Gfx` no longer exposes `render_all()` as the canonical operational frame path. - -### Integration Tests -- runtime tick path renders through `FrameComposer.render_frame()`. -- no-scene and active-scene frame modes both remain valid. -- runtime tick path remains valid when the active scene uses `8x8` tiles. - -### Manual Verification -- Run the repository CI path and confirm the final integrated service model is green. - -## Acceptance Criteria - -- [ ] Frame-loop callsites use `FrameComposer.render_frame()`. -- [ ] `Gfx.render_all()` is retired from the canonical service path. -- [ ] Regression coverage protects against fallback to the old model. -- [ ] Repository validation passes after the migration. -- [ ] Regression coverage includes the canonical `8x8` world-path case. - -## Dependencies - -- Depends on `PLN-0017`, `PLN-0018`, `PLN-0019`, and `PLN-0020` -- Source decision: `DEC-0014` - -## Risks - -- Removing `render_all()` too early can strand intermediate callsites. -- Leaving it in place as a canonical path for too long can create a dual-service model that is harder to remove later. -- Migrating callsites without `8x8` regression coverage can falsely validate only the legacy `16x16` path. diff --git a/discussion/workflow/plans/PLN-0022-composer-syscall-domain-and-spec-propagation.md b/discussion/workflow/plans/PLN-0022-composer-syscall-domain-and-spec-propagation.md deleted file mode 100644 index 34916ad3..00000000 --- a/discussion/workflow/plans/PLN-0022-composer-syscall-domain-and-spec-propagation.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -id: PLN-0022 -ticket: frame-composer-public-syscall-surface -title: Plan - Composer Syscall Domain and Spec Propagation -status: accepted -created: 2026-04-17 -completed: -tags: [gfx, runtime, syscall, abi, spec, isa-core, frame-composer] ---- - -## Objective - -Introduce the canonical `composer.*` syscall domain, define `ComposerOpStatus`, and propagate the new public contract through the canonical spec, ABI documentation, and `ISA_CORE` artifacts where affected. - -## Background - -`DEC-0015` locks the public orchestration surface on `composer.*`, requires `ComposerOpStatus` for mutating composer-domain calls, and requires propagation beyond code into canonical spec, ABI-facing documentation, and `ISA_CORE` where the public syscall surface is described normatively. - -## Scope - -### Included -- add the `composer` syscall domain and ids -- define `ComposerOpStatus` -- remove `gfx.set_sprite(...)` from the public ABI contract -- update canonical spec documentation for the new public surface -- update ABI-facing documentation and `ISA_CORE` wherever the public syscall contract is described - -### Excluded -- runtime dispatch implementation -- cartridge and stress program migration -- final repository-wide CI execution - -## Execution Steps - -### Step 1 - Define the public `composer` syscall contract - -**What:** -Add the new canonical public syscall surface to the HAL syscall contract. - -**How:** -- Extend the syscall enum, registry, metadata, and resolver with a new `composer` domain. -- Allocate explicit syscall ids for: - - `composer.bind_scene` - - `composer.unbind_scene` - - `composer.set_camera` - - `composer.emit_sprite` -- Remove `gfx.set_sprite` from the public syscall contract and registry. -- Keep syscall metadata explicit for arg/ret slots and capability requirements. - -**File(s):** -- `crates/console/prometeu-hal/src/syscalls.rs` -- `crates/console/prometeu-hal/src/syscalls/domains/*` -- `crates/console/prometeu-hal/src/syscalls/registry.rs` -- `crates/console/prometeu-hal/src/syscalls/resolver.rs` - -### Step 2 - Introduce `ComposerOpStatus` - -**What:** -Create the status family for composer-domain mutating operations. - -**How:** -- Define a `ComposerOpStatus` type in HAL with explicit operational states needed by: - - scene binding - - scene unbinding - - sprite emission -- Ensure the enum is semantically composer-domain specific rather than a rename wrapper around `GfxOpStatus`. -- Update public API references so composer syscalls return `ComposerOpStatus` where required by `DEC-0015`. - -**File(s):** -- `crates/console/prometeu-hal/src/*` -- any shared status exports used by runtime/VM code - -### Step 3 - Propagate the contract into spec, ABI docs, and `ISA_CORE` - -**What:** -Update normative documentation so the public contract no longer describes legacy `gfx.set_sprite`. - -**How:** -- Identify canonical spec files that describe VM graphics/composition syscalls. -- Replace public references to legacy sprite orchestration with `composer.*`. -- Update ABI-facing docs to pin: - - namespace - - names - - arg order - - return semantics -- Update `ISA_CORE` if and where it references the affected syscall surface. -- Keep published spec content in English per repository policy. - -**File(s):** -- canonical spec location(s) -- ABI contract documentation -- `ISA_CORE` artifact(s) if affected - -## Test Requirements - -### Unit Tests -- syscall registry tests pin the new `composer.*` entries and reject removed legacy identities -- `ComposerOpStatus` values are pinned where public return semantics are asserted - -### Integration Tests -- declared syscall resolution accepts `composer.*` declarations and rejects removed `gfx.set_sprite` - -### Manual Verification -- inspect canonical spec, ABI docs, and `ISA_CORE` references to confirm the public contract matches `DEC-0015` - -## Acceptance Criteria - -- [ ] The public syscall registry exposes `composer.bind_scene`, `composer.unbind_scene`, `composer.set_camera`, and `composer.emit_sprite`. -- [ ] `ComposerOpStatus` exists as the canonical status family for composer-domain mutating syscalls. -- [ ] `gfx.set_sprite` is removed from the public ABI contract. -- [ ] Canonical spec documentation is updated to describe `composer.*`. -- [ ] ABI-facing docs and `ISA_CORE` are updated wherever the affected public surface is documented. - -## Dependencies - -- Source decision: `DEC-0015` - -## Risks - -- Missing a normative doc location would leave the code and published contract divergent. -- Reusing `GfxOpStatus` semantics by accident would weaken the service-boundary separation required by `DEC-0015`. -- Removing the legacy syscall contract incompletely could leave resolver or ABI ambiguity behind. diff --git a/discussion/workflow/plans/PLN-0023-composer-runtime-dispatch-and-legacy-removal.md b/discussion/workflow/plans/PLN-0023-composer-runtime-dispatch-and-legacy-removal.md deleted file mode 100644 index 70999ee8..00000000 --- a/discussion/workflow/plans/PLN-0023-composer-runtime-dispatch-and-legacy-removal.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -id: PLN-0023 -ticket: frame-composer-public-syscall-surface -title: Plan - Composer Runtime Dispatch and Legacy Removal -status: accepted -created: 2026-04-17 -completed: -tags: [runtime, syscall, frame-composer, dispatch, migration] ---- - -## Objective - -Route the new public `composer.*` syscalls through `FrameComposer`, remove legacy `gfx.set_sprite` handling, and align runtime-side operational behavior with `DEC-0015`. - -## Background - -`DEC-0015` closes the public contract around `composer.*` and requires that `gfx.set_sprite` be removed completely rather than kept as a compatibility shim. The internal `FrameComposer` ownership model already exists from `DEC-0014` and plans `PLN-0017` through `PLN-0021`. - -## Scope - -### Included -- runtime syscall dispatch for `composer.*` -- operational mapping from syscall args to `FrameComposer` -- removal of legacy `gfx.set_sprite` runtime handling -- runtime-facing tests for composer-domain behavior - -### Excluded -- spec and ABI doc propagation -- cartridge/tooling migration -- final `make ci` closure - -## Execution Steps - -### Step 1 - Add runtime dispatch for `composer.*` - -**What:** -Teach VM runtime dispatch to call `FrameComposer` through the new public contract. - -**How:** -- Add dispatch arms for: - - `composer.bind_scene` - - `composer.unbind_scene` - - `composer.set_camera` - - `composer.emit_sprite` -- Parse arguments exactly as pinned by the HAL metadata. -- Return `ComposerOpStatus` for mutating composer-domain syscalls. - -**File(s):** -- `crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs` -- any adjacent runtime helpers - -### Step 2 - Map operational outcomes cleanly onto `ComposerOpStatus` - -**What:** -Make runtime failures and normal outcomes reflect the new composer-domain status model. - -**How:** -- Bind runtime-side operational checks to status outcomes such as: - - scene bank unavailable - - bank invalid - - argument range invalid - - layer invalid - - sprite overflow if surfaced operationally -- Keep non-fatal overflow behavior aligned with `DEC-0015`. - -**File(s):** -- `crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs` -- `crates/console/prometeu-hal/src/*` as needed for shared status meaning - -### Step 3 - Remove legacy `gfx.set_sprite` runtime support - -**What:** -Delete the old public runtime path for slot-style sprite submission. - -**How:** -- Remove dispatch support for `gfx.set_sprite`. -- Remove runtime assumptions about `active`, caller-provided indices, and legacy sprite ABI shape. -- Keep no private compatibility hook behind the public API. - -**File(s):** -- `crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs` -- adjacent tests and public syscall references - -## Test Requirements - -### Unit Tests -- runtime dispatch returns `ComposerOpStatus` for bind, unbind, and emit operations -- `composer.set_camera` stores the minimal V1 camera coordinates correctly - -### Integration Tests -- a VM/runtime test can bind a scene, set camera, emit a sprite, reach `FRAME_SYNC`, and render through the canonical frame path -- public runtime behavior rejects removed `gfx.set_sprite` declarations/calls - -### Manual Verification -- inspect dispatch code to confirm all public orchestration now routes through `FrameComposer` rather than a legacy `gfx` sprite syscall path - -## Acceptance Criteria - -- [ ] Runtime dispatch supports all canonical `composer.*` syscalls. -- [ ] Mutating composer-domain calls return `ComposerOpStatus`. -- [ ] `gfx.set_sprite` is removed from runtime public handling. -- [ ] Runtime tests cover scene bind, camera set, sprite emit, and frame rendering through the public path. - -## Dependencies - -- Depends on `PLN-0022` -- Source decision: `DEC-0015` - -## Risks - -- Removing legacy handling before all runtime references are migrated can strand tests or bytecode fixtures. -- Poor `ComposerOpStatus` mapping could collapse useful operational distinctions into generic failures. diff --git a/discussion/workflow/plans/PLN-0024-composer-cartridge-tooling-and-regression-migration.md b/discussion/workflow/plans/PLN-0024-composer-cartridge-tooling-and-regression-migration.md deleted file mode 100644 index 1e5fb3a1..00000000 --- a/discussion/workflow/plans/PLN-0024-composer-cartridge-tooling-and-regression-migration.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -id: PLN-0024 -ticket: frame-composer-public-syscall-surface -title: Plan - Composer Cartridge, Tooling, and Regression Migration -status: accepted -created: 2026-04-17 -completed: -tags: [runtime, bytecode, tooling, stress, regression, frame-composer] ---- - -## Objective - -Migrate bytecode declarations, cartridges, stress tooling, and regression coverage from legacy public sprite orchestration to the canonical `composer.*` surface. - -## Background - -`DEC-0015` requires the new public composer-domain ABI to land without leaving `gfx.set_sprite` as a fallback. That means the migration must cover the generated bytecode, test cartridges, and stress tooling that still assume the old public contract. - -## Scope - -### Included -- bytecode declaration updates for `composer.*` -- cartridge and stress generator migration -- regression coverage for the public composer-domain path -- removal of legacy syscall usage from test and tooling surfaces - -### Excluded -- canonical spec propagation -- runtime dispatch implementation -- final repository-wide CI closure - -## Execution Steps - -### Step 1 - Migrate declared syscall users and fixtures - -**What:** -Update code and fixtures that declare public syscalls so they target `composer.*`. - -**How:** -- Replace legacy public sprite syscall declarations with composer-domain declarations. -- Update ABI expectations in bytecode-related tests and fixtures. -- Ensure removal of `gfx.set_sprite` is reflected in any declaration validation snapshots. - -**File(s):** -- bytecode tests and fixtures -- syscall declaration users across runtime and tools - -### Step 2 - Migrate stress and cartridge tooling - -**What:** -Make the stress cartridge and related generators exercise the canonical public frame path. - -**How:** -- Update `pbxgen-stress` and any cartridge generators to declare and call `composer.*`. -- Replace legacy sprite-path usage with `composer.emit_sprite`. -- Add scene bind and camera usage where needed so the stress path reaches the real canonical pipeline. - -**File(s):** -- `crates/tools/pbxgen-stress/src/*` -- `test-cartridges/stress-console/*` -- related scripts such as `scripts/run-stress.sh` - -### Step 3 - Expand regression coverage around the public path - -**What:** -Lock the new public orchestration contract with regression tests. - -**How:** -- Add tests that cover: - - composer-domain declaration resolution - - public bind/unbind/camera/emit behavior - - scene rendering through the public path - - stress/tooling integration using `composer.*` -- Ensure no regression fixture still relies on removed `gfx.set_sprite`. - -**File(s):** -- runtime tests -- HAL syscall tests -- tooling tests where available - -## Test Requirements - -### Unit Tests -- bytecode and syscall declaration tests pin `composer.*` names and slot counts - -### Integration Tests -- stress or cartridge-facing tests exercise scene bind, camera set, and sprite emit through `composer.*` -- regression fixtures fail if `gfx.set_sprite` is reintroduced - -### Manual Verification -- inspect generated stress cartridge declarations and program behavior to confirm the public path is truly composer-domain based - -## Acceptance Criteria - -- [ ] Bytecode declarations and fixtures use `composer.*` instead of legacy public sprite orchestration. -- [ ] Stress tooling and test cartridges exercise the canonical public `FrameComposer` path. -- [ ] Regression coverage protects against fallback to `gfx.set_sprite`. - -## Dependencies - -- Depends on `PLN-0022` and `PLN-0023` -- Source decision: `DEC-0015` - -## Risks - -- Partial cartridge/tooling migration could leave the repository with hidden legacy public ABI usage. -- Stress tooling may appear to pass while still missing scene/camera coverage if it only migrates sprite calls. diff --git a/discussion/workflow/plans/PLN-0025-final-ci-validation-and-polish.md b/discussion/workflow/plans/PLN-0025-final-ci-validation-and-polish.md deleted file mode 100644 index fd510d0d..00000000 --- a/discussion/workflow/plans/PLN-0025-final-ci-validation-and-polish.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -id: PLN-0025 -ticket: frame-composer-public-syscall-surface -title: Plan - Final CI Validation and Polish -status: accepted -created: 2026-04-17 -completed: -tags: [ci, validation, regression, polish] ---- - -## Objective - -Run the final repository validation path, including `make ci`, and perform the last compatibility, formatting, lint, and regression fixes required to close the composer-domain migration cleanly. - -## Background - -`DEC-0015` requires a coordinated migration across ABI, runtime, tooling, cartridges, spec, and documentation. After the implementation plans land, the repository still needs a final closure pass so no residual breakage survives in formatting, linting, tests, generated artifacts, or CI expectations. - -## Scope - -### Included -- final repository validation with `make ci` -- fixups required by formatting, lint, tests, snapshots, or generated artifacts -- final consistency pass across migrated files - -### Excluded -- introducing new contract changes beyond `DEC-0015` -- reopening ABI or service-boundary decisions - -## Execution Steps - -### Step 1 - Run the final validation entrypoint - -**What:** -Execute the repository’s final CI validation path. - -**How:** -- Run `make ci` after `PLN-0022`, `PLN-0023`, and `PLN-0024` are complete. -- Capture failures from formatting, lint, tests, coverage setup, generation steps, or artifact drift. - -**File(s):** -- repository-wide validation entrypoints - -### Step 2 - Apply closure fixes without reopening scope - -**What:** -Resolve residual breakage surfaced by final validation. - -**How:** -- Fix formatting and lint issues. -- Update snapshots or generated artifacts only where the migrated public contract requires it. -- Repair any remaining tests or documentation references that fail under `make ci`. -- Do not widen scope beyond the accepted composer-domain migration. - -**File(s):** -- any files directly implicated by final validation failures - -### Step 3 - Confirm final repository consistency - -**What:** -Leave the migration in a stable publishable state. - -**How:** -- Re-run `make ci` until it passes cleanly. -- Verify no legacy public `gfx.set_sprite` usage remains in code, tests, tooling, or docs. -- Confirm the worktree reflects only intended migration changes. - -**File(s):** -- repository-wide - -## Test Requirements - -### Unit Tests -- whatever unit coverage is exercised by `make ci` must remain green - -### Integration Tests -- repository integration coverage under `make ci` must pass after the migration - -### Manual Verification -- inspect the tree for residual `gfx.set_sprite` references and incomplete composer-domain propagation - -## Acceptance Criteria - -- [ ] `make ci` passes after the composer-domain migration family lands. -- [ ] Final fixups do not reopen contract scope beyond `DEC-0015`. -- [ ] No residual public `gfx.set_sprite` usage remains in the repository. - -## Dependencies - -- Depends on `PLN-0022`, `PLN-0023`, and `PLN-0024` -- Source decision: `DEC-0015` - -## Risks - -- If this final closure pass is skipped, small residual regressions can survive across formatting, lint, or generated artifacts even when the core implementation is correct. -- Late fixes can accidentally widen scope unless kept strictly bounded to validation fallout. diff --git a/discussion/workflow/plans/PLN-0026-gfx-overlay-contract-and-spec-propagation.md b/discussion/workflow/plans/PLN-0026-gfx-overlay-contract-and-spec-propagation.md deleted file mode 100644 index ab33e3d4..00000000 --- a/discussion/workflow/plans/PLN-0026-gfx-overlay-contract-and-spec-propagation.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -id: PLN-0026 -ticket: deferred-overlay-and-primitive-composition -title: Plan - GFX Overlay Contract and Spec Propagation -status: accepted -created: 2026-04-18 -completed: -tags: [gfx, runtime, spec, overlay, primitives, hud] ---- - -## Objective - -Propagate `DEC-0016` into the canonical specs and internal contracts so `gfx.*` primitives are defined as deferred final overlay/debug operations outside `FrameComposer`. - -## Background - -`DEC-0016` locks a new semantic boundary: - -- `FrameComposer` remains the owner of canonical game-frame composition; -- `gfx.*` primitives and `draw_text(...)` become deferred final overlay/debug operations; -- that overlay lives outside `FrameComposer` and is drained after `hud_fade`. - -Execution must start by updating the normative contract before implementation changes spread through runtime and drivers. - -## Scope - -### Included -- update canonical runtime/gfx spec text to describe deferred overlay semantics -- update any ABI-facing or developer-facing docs that still imply direct stable writes to `back` -- align local contract comments and module docs where they currently imply immediate-write semantics as the stable model - -### Excluded -- implementation of the overlay subsystem -- runtime frame-end integration -- final repository-wide CI - -## Execution Steps - -### Step 1 - Update canonical graphics/runtime documentation - -**What:** -Publish the new semantic contract for `gfx.*` primitives. - -**How:** -- Update the canonical runtime/gfx spec so `gfx.draw_text(...)` and peer primitives are described as deferred final overlay/debug operations. -- State explicitly that primitives are not part of canonical scene/sprite/HUD composition. -- State the ordering rule that overlay/debug is drained after `hud_fade`. -- Ensure the no-scene and scene-bound paths are described consistently. - -**File(s):** -- canonical runtime/gfx spec files under `docs/specs/runtime/` - -### Step 2 - Align implementation-facing contract text - -**What:** -Remove stale implementation comments that imply immediate stable writes to the framebuffer. - -**How:** -- Inspect module-level comments and trait docs in `hal`, `drivers`, and runtime code for language that now contradicts `DEC-0016`. -- Update only the contract-bearing comments and docs that materially affect maintenance and implementation clarity. - -**File(s):** -- `crates/console/prometeu-hal/src/gfx_bridge.rs` -- `crates/console/prometeu-drivers/src/gfx.rs` -- `crates/console/prometeu-drivers/src/frame_composer.rs` -- runtime-adjacent modules where frame ordering is described - -## Test Requirements - -### Unit Tests -- none required for pure doc propagation - -### Integration Tests -- none required for pure doc propagation - -### Manual Verification -- inspect the updated spec and local contract comments to confirm they no longer describe primitives as stable direct writes to `back` - -## Acceptance Criteria - -- [ ] Canonical spec text describes `gfx.*` primitives as deferred final overlay/debug operations. -- [ ] The spec states that overlay/debug is outside `FrameComposer`. -- [ ] The spec states that overlay/debug is drained after `hud_fade`. -- [ ] Local implementation-facing contract comments no longer imply immediate-write semantics as the stable model. - -## Dependencies - -- Source decision: `DEC-0016` - -## Risks - -- Missing a normative doc location would leave code and published contract divergent. -- Over-editing local comments could unintentionally restate design choices outside the scope of `DEC-0016`. diff --git a/discussion/workflow/plans/PLN-0027-deferred-gfx-overlay-subsystem.md b/discussion/workflow/plans/PLN-0027-deferred-gfx-overlay-subsystem.md deleted file mode 100644 index e746e8fb..00000000 --- a/discussion/workflow/plans/PLN-0027-deferred-gfx-overlay-subsystem.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -id: PLN-0027 -ticket: deferred-overlay-and-primitive-composition -title: Plan - Deferred GFX Overlay Subsystem -status: accepted -created: 2026-04-18 -completed: -tags: [gfx, runtime, overlay, primitives, text, drivers] ---- - -## Objective - -Introduce a dedicated deferred overlay/debug subsystem for `gfx.*` primitives outside `FrameComposer`, with command capture for `draw_text(...)` and the primitive family selected for the first migration. - -## Background - -`DEC-0016` requires primitive/text overlay ownership to remain outside `FrameComposer` while still allowing shared raster helpers and low-level optimizations internally. The new subsystem must preserve semantic separation from scene/sprite/HUD composition. - -## Scope - -### Included -- introduce an overlay/debug command queue or equivalent subsystem outside `FrameComposer` -- route `gfx.draw_text(...)` into deferred command capture instead of stable direct framebuffer writes -- route the chosen V1 primitive family into the same deferred overlay/debug path -- keep raster helper reuse allowed without merging semantic ownership - -### Excluded -- runtime frame-end sequencing -- no-scene/scene parity tests at the runtime level -- final repository-wide CI - -## Execution Steps - -### Step 1 - Define overlay/debug state ownership in drivers - -**What:** -Create the subsystem that owns deferred `gfx.*` overlay/debug commands. - -**How:** -- Add a dedicated owner adjacent to `Gfx`/`Hardware`, but not inside `FrameComposer`. -- Define the minimal command model required for V1 operations. -- Keep the subsystem screen-space and explicitly pipeline-agnostic relative to `composer.*`. - -**File(s):** -- `crates/console/prometeu-drivers/src/*` -- `crates/console/prometeu-hal/src/*` if bridge traits need extension - -### Step 2 - Route text and selected primitives into deferred capture - -**What:** -Stop treating text/primitives as stable direct writes. - -**How:** -- Change `gfx.draw_text(...)` to enqueue deferred overlay/debug work. -- Migrate the selected V1 primitive set into the same deferred path. -- Keep any remaining unmigrated primitives either explicitly out of scope or routed consistently if they are already part of the accepted V1 set. -- Preserve internal raster helper reuse where useful. - -**File(s):** -- `crates/console/prometeu-drivers/src/gfx.rs` -- runtime dispatch call sites that submit `gfx.*` primitives - -### Step 3 - Add local driver-level tests for deferred capture semantics - -**What:** -Prove that overlay/debug commands are captured separately from game composition state. - -**How:** -- Add tests that assert text/primitives do not need direct stable writes to `back` to survive until overlay drain. -- Add tests that assert the overlay owner is independent from `FrameComposer` state. - -**File(s):** -- `crates/console/prometeu-drivers/src/gfx.rs` -- new or existing driver test modules - -## Test Requirements - -### Unit Tests -- command capture tests for `draw_text(...)` -- tests for each migrated V1 primitive class -- tests proving overlay/debug state is owned outside `FrameComposer` - -### Integration Tests -- none in this plan; runtime-level ordering is covered by the next plan - -### Manual Verification -- inspect driver ownership boundaries to confirm `FrameComposer` does not gain overlay/debug state - -## Acceptance Criteria - -- [ ] A dedicated deferred overlay/debug subsystem exists outside `FrameComposer`. -- [ ] `gfx.draw_text(...)` is captured as deferred overlay/debug work. -- [ ] The selected V1 primitive family is captured through the same subsystem. -- [ ] Driver-level tests prove overlay/debug state is operationally separate from canonical game composition state. - -## Dependencies - -- Source decision: `DEC-0016` -- Prefer to execute after `PLN-0026` - -## Risks - -- Accidentally reusing `FrameComposer` storage or state would violate the accepted ownership boundary. -- Migrating only part of the primitive family without explicit scoping could create inconsistent semantics across `gfx.*`. diff --git a/discussion/workflow/plans/PLN-0028-runtime-frame-end-overlay-integration-and-parity.md b/discussion/workflow/plans/PLN-0028-runtime-frame-end-overlay-integration-and-parity.md deleted file mode 100644 index 3fadea8e..00000000 --- a/discussion/workflow/plans/PLN-0028-runtime-frame-end-overlay-integration-and-parity.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -id: PLN-0028 -ticket: deferred-overlay-and-primitive-composition -title: Plan - Runtime Frame-End Overlay Integration and Parity -status: accepted -created: 2026-04-18 -completed: -tags: [runtime, overlay, frame-composer, no-scene, regression, stress] ---- - -## Objective - -Integrate deferred overlay/debug draining into the runtime frame-end sequence so scene-bound and no-scene frames both present the same final `gfx.*` primitive behavior after `hud_fade`. - -## Background - -After `PLN-0027`, the overlay/debug subsystem will exist but still needs to be drained in the correct place relative to `FrameComposer.render_frame()`, fades, and present/present-adjacent behavior. This plan closes the observable runtime semantics required by `DEC-0016`. - -## Scope - -### Included -- runtime frame-end ordering changes -- scene-bound and no-scene parity -- regression coverage for overlay visibility above the canonical game frame -- stress-cartridge adjustments if needed to prove text/primitives now survive frame composition - -### Excluded -- broad renderer optimization work -- final repository-wide CI - -## Execution Steps - -### Step 1 - Insert overlay/debug drain into the frame-end path - -**What:** -Drain deferred overlay/debug after canonical game composition is complete. - -**How:** -- Update the runtime frame-end path so overlay/debug drain occurs after: - - `FrameComposer.render_frame()` - - `scene_fade` - - `hud_fade` -- Ensure the same ordering is respected in the no-scene path. - -**File(s):** -- `crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs` -- `crates/console/prometeu-drivers/src/hardware.rs` -- `crates/console/prometeu-drivers/src/gfx.rs` -- any bridge traits needed by the runtime/hardware path - -### Step 2 - Add runtime and driver regressions for final visual ordering - -**What:** -Lock the new visible behavior. - -**How:** -- Add tests proving `gfx.draw_text(...)` remains visible after scene-backed frame composition. -- Add tests proving the same behavior with no scene bound. -- Add tests proving overlay/debug sits above the canonical game frame rather than being erased by it. - -**File(s):** -- `crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs` -- driver-level render tests where helpful - -### Step 3 - Update stress/integration fixtures if needed - -**What:** -Restore or improve stress scenarios that rely on visible text/primitives. - -**How:** -- Update `pbxgen-stress` or related stress fixtures so text/primitives are once again a valid visible overlay signal. -- Keep the stress focused on the new model rather than reintroducing obsolete immediate-write assumptions. - -**File(s):** -- `crates/tools/pbxgen-stress/src/lib.rs` -- `test-cartridges/stress-console/*` - -## Test Requirements - -### Unit Tests -- local ordering tests where runtime integration depends on helper sequencing - -### Integration Tests -- runtime tests for scene-bound overlay/debug visibility -- runtime tests for no-scene parity -- stress/tooling validation that text or primitives are visible again as final overlay/debug - -### Manual Verification -- run the stress path and visually confirm overlay/debug survives on top of scene/sprites after frame composition - -## Acceptance Criteria - -- [ ] The runtime drains deferred overlay/debug after canonical game composition and after `hud_fade`. -- [ ] Scene-bound and no-scene paths expose the same overlay/debug semantics. -- [ ] Regression tests prove `draw_text(...)` is no longer erased by scene-backed frame composition. -- [ ] Stress/integration fixtures reflect the new final-overlay semantics where applicable. - -## Dependencies - -- Source decision: `DEC-0016` -- Depends on `PLN-0027` - -## Risks - -- If fades are still applied after overlay/debug drain, the visible contract will contradict `DEC-0016`. -- Incomplete parity between scene-bound and no-scene paths would leave runtime behavior mode-dependent. diff --git a/discussion/workflow/plans/PLN-0029-final-overlay-ci-validation-and-polish.md b/discussion/workflow/plans/PLN-0029-final-overlay-ci-validation-and-polish.md deleted file mode 100644 index 085d83c2..00000000 --- a/discussion/workflow/plans/PLN-0029-final-overlay-ci-validation-and-polish.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -id: PLN-0029 -ticket: deferred-overlay-and-primitive-composition -title: Plan - Final Overlay CI Validation and Polish -status: accepted -created: 2026-04-18 -completed: -tags: [ci, overlay, runtime, gfx, validation] ---- - -## Objective - -Run the final repository validation path for the deferred overlay/debug migration and perform the last compatibility, formatting, lint, and regression fixes required to close the thread cleanly. - -## Background - -`DEC-0016` changes visible runtime semantics and touches both specs and code paths around frame composition. A dedicated final-validation plan is needed so the implementation family can close on a clean CI signal rather than leaving integration fallout for later. - -## Scope - -### Included -- full-tree formatting, lint, and test validation -- stress-path smoke validation after overlay integration -- final cleanup fixes required to satisfy CI - -### Excluded -- new feature work outside the accepted overlay/debug migration - -## Execution Steps - -### Step 1 - Run focused validation before full CI - -**What:** -Catch local fallout in the touched areas before the full repository pass. - -**How:** -- Run targeted tests for drivers, runtime, and `pbxgen-stress`. -- Inspect touched files for stale immediate-write assumptions or missed contract updates. - -**File(s):** -- touched files from `PLN-0026` through `PLN-0028` - -### Step 2 - Run final repository CI - -**What:** -Validate the migration end to end. - -**How:** -- Run the repository validation path, including `make ci`. -- Fix any final formatting, lint, test, or generated-fixture fallout caused by the overlay/debug migration. -- Do not widen scope beyond the accepted thread. - -**File(s):** -- repository-wide - -## Test Requirements - -### Unit Tests -- all relevant crate unit tests pass after the migration - -### Integration Tests -- runtime and stress/integration tests pass after the migration -- `make ci` passes - -### Manual Verification -- inspect the tree for residual direct-write assumptions or incomplete overlay propagation - -## Acceptance Criteria - -- [ ] Targeted validation passes for the touched drivers/runtime/stress areas. -- [ ] `make ci` passes after the deferred overlay/debug migration family lands. -- [ ] No residual contract mismatch remains between spec text and code behavior in the touched thread. - -## Dependencies - -- Source decision: `DEC-0016` -- Depends on `PLN-0026`, `PLN-0027`, and `PLN-0028` - -## Risks - -- Final CI may surface unrelated renderer assumptions that still expect immediate-write semantics. -- Generated cartridge fixtures may drift if regeneration is forgotten during earlier plans.