Lesson 259: Bevy Menu UI Flush Receipt on BUILD_RECEIPT (2026)

Direct answer: Before October fest Bevy 0.17 demo promotion, promote bevy_menu_flush_receipt_v1.json proving MenuUiWriterSet runs before MenuUiReaderSet in Update, deferred Commands from menu OnEnter flush before UI hierarchy reads, and Tracy shows no empty-frame hitch on three open/close cycles—distinct from schedule order blog (bevy_schedule_receipt_v1 strategy) and Lesson 245 (wasm panic build_label). Pair the Bevy menu UI flush Tracy preflight (Guide #22).

Lesson hero for Bevy menu UI flush receipt

Why this matters now (October fest demo menus)

October 2026 teams ship Bevy 0.17 Next Fest slices where the title menu hitches one frame on open—sprites look fine, Tracy shows GPU idle while UI reads stale hierarchy because OnEnter(Menu) queued spawns that flush after layout systems run. Engineers blame batching; the fix is writer-before-reader SystemSet order and a Tracy empty-frame check259 files bevy_menu_flush_receipt_v1.json so BUILD_RECEIPT bevy_menu_flush_ok blocks promotion when flush discipline drifts. Schedule order blog owns the whiteboard graph; Help #15 (menu flicker OnEnter) is reactive triage when shipped—this lesson is BUILD_RECEIPT flush proof.

The menu UI flush preflight is the ninety-second gate—259 is the promotion milestone.

Beginner path (sets → flush → Tracy → receipt)

Step Action Success check
1 Name MenuUiWriterSet + MenuUiReaderSet U1 pass
2 chain() writer before reader in Update U2 pass
3 Move despawn/spawn to writer set only U3 pass
4 Tracy capture: 3× open/close U4 empty-frame check
5 File receipt + BUILD_RECEIPT bevy_menu_flush_ok: true

Time: ~52 minutes first fest candidate; ~15 minutes when sets are pinned.

Developer path (gates U1–U6)

Gate Check Fail when
U1 Menu systems in named SystemSets Anonymous systems in Update
U2 MenuUiWriterSet.before(MenuUiReaderSet) Reader runs same frame as writer
U3 Commands spawn/despawn only in writer set Reader queries entities mid-spawn
U4 Tracy: no hitch span > 1 empty frame on open Visible flicker with idle GPU
U5 menu_flush_pipeline_v1.json committed Ad-hoc README only
U6 Receipt + BUILD_RECEIPT Promote before U4 GREEN

U1 — cousin receipt crosswalk

Field Cousin (blog) Cousin (245) This lesson (259)
Schema bevy_schedule_receipt_v1 playtest_crash_label_receipt_v1 bevy_menu_flush_receipt_v1
Scope Plugin/schedule graph Wasm panic label UI command flush + Tracy
Path release-evidence/bevy/schedule/ release-evidence/bevy/playtest-crash/ release-evidence/bevy/menu-flush/

Do not merge schemas—reference paths in cousin_receipts only.

SystemSet chain (writer-before-reader)

#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
pub enum MenuUiWriterSet {}

#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
pub enum MenuUiReaderSet {}

pub fn configure_menu_flush_sets(app: &mut App) {
    app.configure_sets(
        Update,
        (
            MenuUiWriterSet,
            MenuUiReaderSet,
        )
            .chain(),
    )
    .add_systems(Update, menu_on_enter_spawn.in_set(MenuUiWriterSet))
    .add_systems(Update, menu_layout_read.in_set(MenuUiReaderSet));
}

Rule: anything that despawns menu nodes or inserts Node bundles stays in MenuUiWriterSet; layout/measurement stays in MenuUiReaderSet.

menu_flush_pipeline_v1.json

{
  "schema": "menu_flush_pipeline_v1",
  "build_label": "fest-demo-2026-10-rc4",
  "bevy_version": "0.17",
  "stage": "Update",
  "system_sets": {
    "writer": "MenuUiWriterSet",
    "reader": "MenuUiReaderSet",
    "chain": ["MenuUiWriterSet", "MenuUiReaderSet"]
  },
  "on_enter_menu_uses_commands": true,
  "deferred_flush_strategy": "writer_before_reader_same_stage",
  "tracy_capture_seconds": 90,
  "open_close_cycles_required": 3
}

Pin under release-evidence/bevy/menu-flush/MENU_FLUSH_PIPELINE.json.

U4 — Tracy empty-frame check

  1. Enable trace_tracy + diagnostics preflight budget table.
  2. Name spans: MenuOpen, MenuUiWriter, MenuUiReader, MenuClose.
  3. Pass: open hitch shows work inside MenuUiWriter span, not empty frame between writer and reader.
  4. Archive tracy_menu_flush_90s.csv + screenshot.

bevy_menu_flush_receipt_v1.json

{
  "schema": "bevy_menu_flush_receipt_v1",
  "build_label": "fest-demo-2026-10-rc4",
  "pipeline_path": "release-evidence/bevy/menu-flush/MENU_FLUSH_PIPELINE.json",
  "pipeline_sha256": "ee55…",
  "tracy_artifact": "release-evidence/bevy/menu-flush/tracy_menu_flush_90s.csv",
  "cousin_receipts": {
    "schedule_order_blog": "release-evidence/bevy/schedule/BEVY_SCHEDULE_RECEIPT.json",
    "playtest_crash_label": "release-evidence/bevy/playtest-crash/PLAYTEST_CRASH_LABEL_RECEIPT.json"
  },
  "gates": {
    "U1_named_sets": "pass",
    "U2_writer_before_reader": "pass",
    "U3_commands_in_writer_only": "pass",
    "U4_tracy_empty_frame": "pass",
    "U5_pipeline_committed": "pass",
    "U6_build_receipt": "pass"
  },
  "bevy_menu_flush_ok": true,
  "fest_demo_promotion_allowed": true
}

Pin under release-evidence/bevy/menu-flush/BEVY_MENU_FLUSH_RECEIPT.json.

BUILD_RECEIPT row (U6)

Column Pass when
bevy_menu_flush bevy_menu_flush_ok: true
bevy_schedule_order Cousin blog receipt independent
playtest_crash_label Cousin 245 independent
ALTER TABLE release_publish_gate ADD COLUMN IF NOT EXISTS
  bevy_menu_flush_blocked BOOLEAN NOT NULL DEFAULT false;

Thursday row reviewBevy menu flush line: writer→reader Y/N.

Key takeaways

  1. Writer before reader in the same Update stage (U2)—not “eventually.”
  2. OnEnter(Menu) + Commands is a flush problem, not a sprite problem (U3).
  3. Schedule order blog — graph; 259 — flush receipt on BUILD_RECEIPT.
  4. Tracy diagnostics preflight — capture discipline.
  5. Lesson 245 — crash label cousin.
  6. Hot reload help — masquerades as menu hitch.
  7. Forward: Lesson 260 UGS/OTLP diligence (queued).
  8. Lesson 258 — parallel playtest ingest column.
  9. October capstone 265 wires 254–264 including this row.
  10. Wednesday smoke — 60 s menu loop.

Common mistakes

  • Putting layout read in the same set as despawn (U3).
  • Filing blog schedule receipt while flush receipt RED—different gates.
  • Tracy on Update root only—no MenuUiWriter span (U4).
  • Menu animation in FixedUpdate, input in Update—see blog fixed-timestep section.
  • Merging bevy_menu_flush_receipt into bevy_schedule_receipt.

Troubleshooting

Symptom Lane
One-frame flicker on open U2/U3 chain + Help #15 when live
Hitch every N frames Blog fixed timestep vs UI
Hitch after plugin add Blog plugin order B4
Wasm-only hitch wasm boot preflight
Panic on menu open Lesson 245

Mini exercise (50 minutes)

  1. Reproduce flicker with reader before writer—confirm U4 fail.
  2. Apply chain()—confirm Tracy writer span precedes reader.
  3. Commit menu_flush_pipeline_v1.json.
  4. File receipt; BUILD_RECEIPT GREEN.

Continuity — October–Q4 2026 fest ops truth (254–265)

Lesson Receipt focus
258 OBS replay buffer uniform fragments
259 (this) Bevy menu UI command flush
260 Unity UGS/OTLP diligence (queued)
265 October ops capstone (queued)

Previous: Lesson 258 — OBS replay buffer uniform
Next: Lesson 260 — Unity UGS/OTLP diligence (publish on next Course-Create pass)

FAQ

Same as schedule order blog?
Blog = plugin graph + bevy_schedule_receipt; 259 = writer/reader flush + Tracy on BUILD_RECEIPT.

Same as Lesson 245?
245 = wasm panic build_label; 259 = menu UI flush.

Need Tracy for promotion?
U4 requires named spans + empty-frame check—screenshot alone is not enough.

Help #15?
Reactive OnEnter flicker fix when published—run 259 before fest branch promotion.


Fest demos fail the “polish” bar when menu open hitches one empty frame—chain writer before reader, flush Commands in the writer set, prove it in Tracy, then bevy_menu_flush_ok before promotion.