You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Runbook spec:docs/src/runbook/phases/phase-4.md (committed with the roadmap)
Summary
Write RFC-0007 deciding the command-buffer opcode format, shared-memory/ownership model, per-frame drain semantics, versioned fn-table entry point shape, and the deprecation path for the existing 682 flat #[no_mangle] exports.
Architecture Context
Layer: FFI (Layer 5) — decision record covering goud_engine/src/ffi/ as a whole (see tools/lint_layers.rs — downward-only deps). Modules/types touched:
docs/rfcs/RFC-0007-command-buffer-ffi.md (new) — decision record, no code change in this issue.
Informs (does not modify) goud_engine/src/ffi/ (682 exports), codegen/ffi_manifest.json, codegen/gen_jni.py.
Boundary constraints (only those that apply):
FFI exports: #[no_mangle] extern "C", #[repr(C)], null checks, // SAFETY: comments — this RFC must specify how the new opcode/fn-table surface keeps these invariants.
Generated *.g.rs/*.g.cs/*.g.ts are never hand-edited; change Rust + FFI + schema, rerun ./codegen.sh.
Pattern to follow:docs/rfcs/RFC-0000-template.md structure; existing RFC-0001..0004 for tone/format. GDExtension's versioned get_proc_address-style entry point is the reference model for the fn-table decision.
Scope
RFC-0007 decides: packed opcode/command format (op codes for component write/read-request, transform set, spawn/despawn, draw-submit), shared-memory ownership (who allocates the command buffer, lifetime), drain semantics (one engine-side lock acquisition per frame), and fn-table versioning scheme (goud_get_interface(version) returning a fn-pointer struct).
RFC specifies bulk transform-upload ops writing directly into the ENG2-P2-13 SoA transform store, plus a 0-alloc decode path for the command buffer.
RFC states the deprecation path for per-object flat exports — no compat shims kept long-term (binding user decision; breaking changes land, Throne is fixed after).
Decision list amendments folded back into RFC-0005 (ENG2-P0-09) and RFC-0006 (ENG2-P3-01) where the SoA/columnar transform view is a shared dependency.
Tests: N/A (decision record) — acceptance is RFC merged + reviewed, not a spec test.
Docs/rules updates: .agents/rules/ffi-patterns.md gets an addendum once the RFC lands, pointing at the command-buffer/fn-table pattern.
Acceptance Criteria
RFC-0007 merged to docs/rfcs/, following the RFC-0000 template, with an explicit decision (not just options) on opcode format, ownership, drain semantics, and fn-table versioning.
RFC explicitly states the 0-alloc decode path requirement and the P2-13 SoA write-through requirement.
RFC explicitly states no compat shim is kept for deprecated per-object exports, and names the Throne follow-up issues (THR-A-01, THR-A-02).
None directly — this issue is a decision record with no code change. It authorizes the breaking changes executed in ENG2-P4-02/03/05; those issues carry the Throne follow-up links (THR-A-01, THR-A-02).
Verified current state: grep -rn "no_mangle" goud_engine/src/ffi/ | wc -l = 682 flat #[no_mangle] exports today, plus ~1347 generated JNI functions (gen_jni.py output), with no versioning or bulk submission surface.
Batch APIs already prove the amortization pattern this RFC generalizes: goud_component_add_batch in goud_engine/src/ffi/component/batch.rs:72 takes one lock, then loops the entity slice internally (goud_engine/src/ffi/component/batch.rs:145-172 region) instead of one FFI crossing per entity. The command-buffer design should read this as the existing proof-of-concept for "one lock per frame."
Per design-expansion.md (ENG2-P2-13 note): "ENG2-P4-01 (RFC-0007) MUST specify bulk transform-upload ops writing directly into the P2-13 SoA store, and a 0-alloc decode path." Do not treat this as optional — it's the acceptance-relevant coupling to ENG2-P2-13 and ENG2-P4-04.
Phase 4 as a whole requires the Phase 2 AND Phase 3 gates to have passed before execution starts (design-technical.md:292), even though this issue's own Blocked-By is empty — the RFC can be drafted in parallel, but implementation issues (P4-02 onward) assume the render-core and data-core rewrites are gated green.
roadmap-inputs.md decision 1 is binding: breaking the FFI API is acceptable, no legacy shims are kept long-term. This RFC is where that decision gets made concrete for the FFI surface specifically.
Parent
eng2-p4-ffi-v2)docs/src/runbook/phases/phase-4.md(committed with the roadmap)Summary
Write RFC-0007 deciding the command-buffer opcode format, shared-memory/ownership model, per-frame drain semantics, versioned fn-table entry point shape, and the deprecation path for the existing 682 flat
#[no_mangle]exports.Architecture Context
Layer: FFI (Layer 5) — decision record covering
goud_engine/src/ffi/as a whole (seetools/lint_layers.rs— downward-only deps).Modules/types touched:
docs/rfcs/RFC-0007-command-buffer-ffi.md(new) — decision record, no code change in this issue.goud_engine/src/ffi/(682 exports),codegen/ffi_manifest.json,codegen/gen_jni.py.Boundary constraints (only those that apply):
#[no_mangle] extern "C",#[repr(C)], null checks,// SAFETY:comments — this RFC must specify how the new opcode/fn-table surface keeps these invariants.*.g.rs/*.g.cs/*.g.tsare never hand-edited; change Rust + FFI + schema, rerun./codegen.sh.Pattern to follow:
docs/rfcs/RFC-0000-template.mdstructure; existing RFC-0001..0004 for tone/format. GDExtension's versionedget_proc_address-style entry point is the reference model for the fn-table decision.Scope
goud_get_interface(version)returning a fn-pointer struct)..agents/rules/ffi-patterns.mdgets an addendum once the RFC lands, pointing at the command-buffer/fn-table pattern.Acceptance Criteria
docs/rfcs/, following the RFC-0000 template, with an explicit decision (not just options) on opcode format, ownership, drain semantics, and fn-table versioning.cargo check && cargo fmt --all -- --check && cargo clippy -- -D warningsclean (no code changes expected, but gate stays green).Breaking Change & Throne Follow-up
None directly — this issue is a decision record with no code change. It authorizes the breaking changes executed in ENG2-P4-02/03/05; those issues carry the Throne follow-up links (THR-A-01, THR-A-02).
Blocked By
None.
Files Likely Touched
docs/rfcs/RFC-0007-command-buffer-ffi.md(new)Agent Notes
grep -rn "no_mangle" goud_engine/src/ffi/ | wc -l= 682 flat#[no_mangle]exports today, plus ~1347 generated JNI functions (gen_jni.pyoutput), with no versioning or bulk submission surface.goud_component_add_batchingoud_engine/src/ffi/component/batch.rs:72takes one lock, then loops the entity slice internally (goud_engine/src/ffi/component/batch.rs:145-172region) instead of one FFI crossing per entity. The command-buffer design should read this as the existing proof-of-concept for "one lock per frame."design-expansion.md(ENG2-P2-13 note): "ENG2-P4-01 (RFC-0007) MUST specify bulk transform-upload ops writing directly into the P2-13 SoA store, and a 0-alloc decode path." Do not treat this as optional — it's the acceptance-relevant coupling to ENG2-P2-13 and ENG2-P4-04.design-technical.md:292), even though this issue's own Blocked-By is empty — the RFC can be drafted in parallel, but implementation issues (P4-02 onward) assume the render-core and data-core rewrites are gated green.roadmap-inputs.mddecision 1 is binding: breaking the FFI API is acceptable, no legacy shims are kept long-term. This RFC is where that decision gets made concrete for the FFI surface specifically.Verification