backend-infra-engineer: Post v0.3.9-hotfix7 snapshot (build cleanup)

This commit is contained in:
scawful
2025-12-22 00:20:49 +00:00
parent 2934c82b75
commit 5c4cd57ff8
1259 changed files with 239160 additions and 43801 deletions

View File

@@ -11,6 +11,16 @@ YAZE includes two primary AI assistance modes:
Both modes use the same underlying AI service (Ollama or Gemini) and tool infrastructure, but target different workflows.
## Choosing the right agent persona
- Personas live in `.claude/agents/<agent-id>.md`; open the matching file as your system prompt before a session (available to all agents, not just Claude).
- **ai-infra-architect**: AI/agent infra, MCP/gRPC, z3ed tooling, model plumbing.
- **backend-infra-engineer**: Build/packaging/toolchains, CI reliability, release plumbing.
- **imgui-frontend-engineer**: ImGui/editor UI, renderer/backends, canvas/docking UX.
- **snes-emulator-expert**: Emulator core (CPU/APU/PPU), performance/accuracy/debugging.
- **zelda3-hacking-expert**: Gameplay/ROM logic, data formats, hacking workflows.
- **test-infrastructure-expert**: Test harnesses, CTest/gMock infra, flake/bloat triage.
- **docs-janitor**: Docs/process hygiene, onboarding, checklists.
## Prerequisites
### Build Requirements
@@ -78,7 +88,7 @@ cmake --build --preset mac-ai --target z3ed
```bash
# You encounter a compilation error
cmake --build build_ai
cmake --build build
# [ERROR] src/app/gfx/snes_color.cc:45: error: 'Arena' was not declared
# Use z3ed to analyze and suggest fixes
@@ -141,7 +151,7 @@ The agent automatically analyzes compilation failures:
```bash
z3ed agent chat --rom zelda3.sfc
> cmake --build build_ai failed with:
> cmake --build build failed with:
> error: 'gfx::Arena' has not been declared in snes_color.cc:45
# AI will:

View File

@@ -2,25 +2,33 @@
This guide summarizes the architecture and implementation standards used across the editor codebase.
## Editor Status (October 2025)
## Editor Status (November 2025)
| Editor | State | Notes |
|-------------------|--------------|-------|
| Overworld | Stable | Full feature set; continue regression testing after palette fixes. |
| Message | Stable | Re-test rendering after recent palette work. |
| Emulator | Stable | UI and core subsystems aligned with production builds. |
| Palette | Stable | Serves as the source of truth for palette helpers. |
| Assembly | Stable | No outstanding refactors. |
| Dungeon | Experimental | Requires thorough manual coverage before release. |
| Graphics | Experimental | Large rendering changes in flight; validate texture pipeline. |
| Sprite | Experimental | UI patterns still migrating to the new card system. |
| Editor | State | Panels | Notes |
|-------------------|--------------|--------|-------|
| Overworld | Stable | 8 | Full feature set with tile16 editor, scratch space. |
| Message | Stable | 4 | Message list, editor, font atlas, dictionary panels. |
| Emulator | Stable | 10 | CPU, PPU, Memory debuggers; AI agent integration. |
| Palette | Stable | 11 | Source of truth for palette helpers. |
| Assembly | Stable | 2 | File browser and editor panels. |
| Dungeon | Stable | 8 | Room selector, matrix, graphics, object editor. |
| Graphics | Stable | 4 | Sheet editor, browser, player animations. |
| Sprite | Stable | 2 | Vanilla and custom sprite panels. |
| Screen | Stable | 5 | Dungeon maps, inventory, title screen, etc. |
| Music | Experimental | 3 | Tracker, instrument editor, assembly view. |
### Screen Editor Notes
### Recent Improvements (v0.3.9)
- **Title screen**: Vanilla ROM tilemap parsing remains broken. Implement a DMA
parser and confirm the welcome screen renders before enabling painting.
- **Overworld map**: Mode 7 tiling, palette switching, and custom map import/export are in place. The next milestone is faster tile painting.
- **Dungeon map**: Rendering is wired up; tile painting and ROM write-back are still pending.
- **EditorManager Refactoring**: 90% feature parity with 44% code reduction
- **Panel-Based UI**: All 34 editor panels (formerly cards) with X-button close, multi-session support
- **SDL3 Backend Infrastructure**: 17 abstraction files for future migration
- **WASM Web Port**: Real-time collaboration via WebSocket
- **AI Agent Tools**: Phases 1-4 complete (meta-tools, schemas, validation)
### Known Issues
- **Dungeon object rendering**: Regression with object visibility
- **ZSOW v3 palettes**: Large-area palette issues being investigated
## 1. Core Architectural Patterns
@@ -139,18 +147,18 @@ Google-style C++23 guidelines while accommodating ROM hacking patterns.
### 5.1. Quick Debugging with Startup Flags
To accelerate your debugging workflow, use command-line flags to jump directly to specific editors and open relevant UI cards:
To accelerate your debugging workflow, use command-line flags to jump directly to specific editors and open relevant UI panels:
```bash
# Quick dungeon room testing
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0"
# Compare multiple rooms
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0,Room 1,Room 105"
# Full dungeon workspace
./yaze --rom_file=zelda3.sfc --editor=Dungeon \
--cards="Rooms List,Room Matrix,Object Editor,Palette Editor"
--open_panels="Rooms List,Room Matrix,Object Editor,Palette Editor"
# Enable debug logging
./yaze --debug --log_file=debug.log --rom_file=zelda3.sfc --editor=Dungeon
@@ -158,9 +166,9 @@ To accelerate your debugging workflow, use command-line flags to jump directly t
**Available Editors**: Assembly, Dungeon, Graphics, Music, Overworld, Palette, Screen, Sprite, Message, Hex, Agent, Settings
**Dungeon Editor Cards**: Rooms List, Room Matrix, Entrances List, Room Graphics, Object Editor, Palette Editor, Room N (where N is room ID 0-319)
**Dungeon Editor Panels**: Rooms List, Room Matrix, Entrances List, Room Graphics, Object Editor, Palette Editor, Room N (where N is room ID 0-319)
See [debugging-startup-flags.md](debugging-startup-flags.md) for complete documentation.
See [Startup Debugging Flags](debug-flags.md) for complete documentation, including panel visibility overrides (`--startup_welcome/--startup_dashboard/--startup_sidebar`).
### 5.2. Testing Strategies

View File

@@ -1,6 +1,6 @@
# YAZE Startup Debugging Flags
This guide explains how to use command-line flags to quickly open specific editors and cards during development for faster debugging workflows.
This guide explains how to use command-line flags to quickly open specific editors and panels during development for faster debugging workflows.
## Basic Usage
@@ -24,6 +24,13 @@ Enable debug logging with verbose output.
./yaze --debug --log_file=yaze_debug.log
```
### `--log_level`, `--log_categories`, `--log_to_console`
Control verbosity and filter by subsystem. Categories can be allowlisted or blocked by prefixing with `-`:
```bash
./yaze --log_level=debug --log_categories="EditorManager,-Audio" --log_to_console
```
### `--editor`
Open a specific editor on startup. This saves time by skipping manual navigation through the UI.
@@ -46,10 +53,12 @@ Open a specific editor on startup. This saves time by skipping manual navigation
./yaze --rom_file=zelda3.sfc --editor=Dungeon
```
### `--cards`
Open specific cards/panels within an editor. Most useful with the Dungeon editor.
### `--open_panels`
Open specific panels within an editor. Matching is case-insensitive and accepts either display names
or stable panel IDs (e.g., `dungeon.room_list`, `emulator.cpu_debugger`). `Room N` tokens will open
the corresponding dungeon room card.
**Dungeon Editor Cards:**
**Dungeon Editor Panels:**
- `Rooms List` - Shows the list of all dungeon rooms
- `Room Matrix` - Shows the dungeon room layout matrix
- `Entrances List` - Shows dungeon entrance configurations
@@ -60,7 +69,15 @@ Open specific cards/panels within an editor. Most useful with the Dungeon editor
**Example:**
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Rooms List,Room 0"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Rooms List,Room 0"
```
### `--startup_welcome`, `--startup_dashboard`, `--startup_sidebar`
Control startup chrome visibility. Each accepts `auto`, `show`, or `hide`:
```bash
./yaze --rom_file=zelda3.sfc --editor=Overworld \
--startup_welcome=hide --startup_dashboard=show --startup_sidebar=hide
```
## Common Debugging Scenarios
@@ -69,14 +86,14 @@ Open specific cards/panels within an editor. Most useful with the Dungeon editor
Open a specific dungeon room for testing:
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room Graphics"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0,Room Graphics"
```
### 2. Multiple Room Comparison
Compare multiple rooms side-by-side:
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0,Room 1,Room 105"
```
### 3. Full Dungeon Editor Workspace
@@ -84,7 +101,7 @@ Open all dungeon editor tools:
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon \
--cards="Rooms List,Room Matrix,Room Graphics,Object Editor,Palette Editor"
--open_panels="Rooms List,Room Matrix,Room Graphics,Object Editor,Palette Editor"
```
### 4. Debug Mode with Logging
@@ -92,7 +109,7 @@ Enable full debug output while working:
```bash
./yaze --rom_file=zelda3.sfc --debug --log_file=debug.log \
--editor=Dungeon --cards="Room 0"
--editor=Dungeon --open_panels="Room 0"
```
### 5. Quick Overworld Editing
@@ -123,16 +140,15 @@ All flags can be combined for powerful debugging setups:
--debug \
--log_file=room_105_debug.log \
--editor=Dungeon \
--cards="Room 105,Room Graphics,Palette Editor,Object Editor"
--open_panels="Room 105,Room Graphics,Palette Editor,Object Editor"
```
## Notes
- Card names are case-sensitive and must match exactly
- Use quotes around comma-separated card lists
- Invalid editor or card names will be logged as warnings but won't crash the application
- The `--cards` flag is currently only implemented for the Dungeon editor
- Room IDs range from 0-319 in the vanilla game
- Panel tokens are matched case-insensitively against IDs and display names
- Use quotes around comma-separated panel lists
- Invalid editor or panel names will be logged as warnings but won't crash the application
- `Room N` shortcuts use the dungeon room ID range (0-319 in vanilla)
## Troubleshooting
@@ -141,11 +157,10 @@ All flags can be combined for powerful debugging setups:
- Verify ROM loaded successfully
- Check log output with `--debug`
**Cards don't appear:**
**Panels don't appear:**
- Ensure editor is set (e.g., `--editor=Dungeon`)
- Check card name spelling
- Some cards require a loaded ROM
**Want to add more card support?**
See `EditorManager::OpenEditorAndCardsFromFlags()` in `src/app/editor/editor_manager.cc`
- Check panel name spelling
- Some panels require a loaded ROM
**Want to add more panel support?**
See `EditorManager::OpenEditorAndPanelsFromFlags()` in `src/app/editor/editor_manager.cc`

View File

@@ -1,6 +1,6 @@
# E5 - Debugging and Testing Guide
**Last Updated**: October 9, 2025
**Last Updated**: December 5, 2025
**Status**: Active
This document provides a comprehensive guide to debugging and testing the `yaze` application. It covers strategies for developers and provides the necessary information for AI agents to interact with, test, and validate the application.
@@ -30,20 +30,19 @@ Categories allow you to filter logs to focus on a specific subsystem. Common cat
You can control logging behavior using command-line flags when launching `yaze` or `yaze_test`.
- **Enable Verbose Debug Logging**:
- **Set Log Level & Categories** (allowlist or blocklist by prefixing with `-`; tokens are trimmed and case-sensitive):
```bash
./build/bin/yaze --debug
./build/bin/yaze --log_level=debug --log_categories="OverworldEditor,-Audio"
```
- **Log to a File**:
- **Log to a File (and mirror to console if needed)**:
```bash
./build/bin/yaze --log_file=yaze_debug.log
./build/bin/yaze --log_file=yaze_debug.log --log_to_console
```
- **Filter by Category**:
- **Enable Verbose Debug Logging** (forces console logging and `debug` level):
```bash
# Only show logs from the APU and CPU emulator components
./build/bin/yaze_emu --emu_debug_apu=true --emu_debug_cpu=true
./build/bin/yaze --debug --log_file=yaze_debug.log
```
**Best Practice**: When debugging a specific component, add detailed `LOG_DEBUG` statements with a unique category. Then, run `yaze` with the appropriate flags to isolate the output.
@@ -66,35 +65,33 @@ The `yaze` ecosystem provides several executables and flags to streamline testin
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness
```
- **Open a Specific Editor and Cards**: To quickly test a specific editor and its components, use the `--editor` and `--cards` flags. This is especially useful for debugging complex UIs like the Dungeon Editor.
- **Open a Specific Editor and Panels**: Use `--editor` and `--open_panels` (panel IDs or display names, comma-separated, case-insensitive) to land exactly where you need. Combine with startup visibility flags to hide chrome for automation:
```bash
# Open the Dungeon Editor with the Room Matrix and two specific room cards
./build/bin/yaze --rom_file zelda3.sfc --editor=Dungeon --cards="Room Matrix,Room 0,Room 105"
# Available editors: Assembly, Dungeon, Graphics, Music, Overworld, Palette,
# Screen, Sprite, Message, Hex, Agent, Settings
# Dungeon editor cards: Rooms List, Room Matrix, Entrances List, Room Graphics,
# Object Editor, Palette Editor, Room N (where N is room ID)
# Open the Dungeon editor with a couple panels pre-visible
./build/bin/yaze --rom_file zelda3.sfc --editor=Dungeon --open_panels="dungeon.room_list,Room 105"
# You can also hide startup chrome for automation runs
./build/bin/yaze --rom_file zelda3.sfc --editor=Overworld \
--open_panels="overworld.map_canvas" --startup_welcome=hide --startup_dashboard=hide
```
**Quick Examples**:
```bash
# Fast dungeon room testing
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0"
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0"
# Compare multiple rooms side-by-side
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0,Room 1,Room 105"
# Full dungeon workspace with all tools
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon \
--cards="Rooms List,Room Matrix,Object Editor,Palette Editor"
--open_panels="Rooms List,Room Matrix,Object Editor,Palette Editor"
# Jump straight to overworld editing
./build/bin/yaze --rom_file=zelda3.sfc --editor=Overworld
```
For a complete reference, see [docs/debugging-startup-flags.md](debugging-startup-flags.md).
For a complete reference, see [Startup Debugging Flags](debug-flags.md).
### Running Automated C++ Tests
@@ -102,22 +99,22 @@ The `yaze_test` executable is used to run the project's suite of unit, integrati
- **Run All Tests**:
```bash
./build_ai/bin/yaze_test
./build/bin/yaze_test
```
- **Run Specific Categories**:
```bash
# Run only fast, dependency-free unit tests
./build_ai/bin/yaze_test --unit
./build/bin/yaze_test --unit
# Run tests that require a ROM file
./build_ai/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
./build/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
```
- **Run GUI-based E2E Tests**:
```bash
# Run E2E tests and watch the GUI interactions
./build_ai/bin/yaze_test --e2e --show-gui
./build/bin/yaze_test --e2e --show-gui
```
### Inspecting ROMs with `z3ed`
@@ -168,7 +165,7 @@ This will return a list of widget IDs (e.g., `Dungeon/Canvas/Map`) that can be u
**Tip**: You can also launch `yaze` with the `--editor` flag to automatically open a specific editor:
```bash
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness --editor=Dungeon --cards="Room 0"
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness --editor=Dungeon --open_panels="Room 0"
```
#### Step 3: Record or Write a Test Script
@@ -193,7 +190,7 @@ An agent can either generate a test script from scratch or use a pre-recorded on
```bash
# Start yaze with the room already open
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness \
--editor=Dungeon --cards="Room 105"
--editor=Dungeon --open_panels="Room 105"
# Then your test script just needs to validate the state
{"action": "assert_visible", "target": "Room Card 105"}

View File

@@ -1248,8 +1248,8 @@ class MusicEditor {
```bash
cd /Users/scawful/Code/yaze
cmake --build build_ai --target yaze -j12
./build_ai/bin/yaze.app/Contents/MacOS/yaze
cmake --build build --target yaze -j12
./build/bin/yaze.app/Contents/MacOS/yaze
```
### Platform-Specific

View File

@@ -1,4 +1,8 @@
# G5 - GUI Consistency and Card-Based Architecture Guide
# G5 - GUI Consistency and Panel-Based Architecture Guide
> Note: The project is migrating from **Card** terminology to **Panel**
> (`PanelWindow`, `PanelManager`, `PanelDescriptor`). This guide still shows
> legacy names; mentally substitute Panel for Card while Phase 2 lands.
This guide establishes standards for GUI consistency across all yaze editors, focusing on the modern card-based architecture, theming system, and layout patterns.
@@ -11,13 +15,14 @@ This guide establishes standards for GUI consistency across all yaze editors, fo
5. [GUI Library Architecture](#5-gui-library-architecture)
6. [Themed Widget System](#6-themed-widget-system)
7. [Begin/End Patterns](#7-beginend-patterns)
8. [Currently Integrated Editors](#8-currently-integrated-editors)
9. [Layout Helpers](#9-layout-helpers)
10. [Workspace Management](#10-workspace-management)
11. [Future Editor Improvements](#11-future-editor-improvements)
12. [Migration Checklist](#12-migration-checklist)
13. [Code Examples](#13-code-examples)
14. [Common Pitfalls](#14-common-pitfalls)
8. [Avoiding Duplicate Rendering](#8-avoiding-duplicate-rendering)
9. [Currently Integrated Editors](#9-currently-integrated-editors)
10. [Layout Helpers](#10-layout-helpers)
11. [Workspace Management](#11-workspace-management)
12. [Future Editor Improvements](#12-future-editor-improvements)
13. [Migration Checklist](#13-migration-checklist)
14. [Code Examples](#14-code-examples)
15. [Common Pitfalls](#15-common-pitfalls)
## 1. Introduction
@@ -532,17 +537,43 @@ if (ImGui::BeginTable("##MyTable", 3, ImGuiTableFlags_Borders)) {
}
```
**Child Window Pattern:**
**Child Window Pattern (CRITICAL):**
⚠️ **This is the most commonly misused pattern.** Unlike tables, `EndChild()` must ALWAYS be called after `BeginChild()`, regardless of the return value.
```cpp
// ✅ CORRECT: EndChild OUTSIDE the if block
if (ImGui::BeginChild("##ScrollRegion", ImVec2(0, 200), true)) {
// Scrollable content
// Scrollable content - only drawn when visible
for (int i = 0; i < 100; i++) {
ImGui::Text("Item %d", i);
}
}
ImGui::EndChild();
ImGui::EndChild(); // ALWAYS called, even if BeginChild returned false
// ❌ WRONG: EndChild INSIDE the if block - causes state corruption!
if (ImGui::BeginChild("##ScrollRegion", ImVec2(0, 200), true)) {
for (int i = 0; i < 100; i++) {
ImGui::Text("Item %d", i);
}
ImGui::EndChild(); // BUG: Not called when BeginChild returns false!
}
```
**Why this matters:** When `BeginChild()` returns false (child window is clipped or not visible), ImGui still expects `EndChild()` to be called to properly clean up internal state. Failing to call it corrupts ImGui's window stack, which can cause seemingly unrelated errors like table assertions or missing UI elements.
**Pattern Comparison:**
| Function | Call End when Begin returns false? |
|----------|-----------------------------------|
| `BeginChild()` / `EndChild()` | ✅ **YES - ALWAYS** |
| `Begin()` / `End()` (windows) | ✅ **YES - ALWAYS** |
| `BeginTable()` / `EndTable()` | ❌ **NO - only if Begin returned true** |
| `BeginTabBar()` / `EndTabBar()` | ❌ **NO - only if Begin returned true** |
| `BeginTabItem()` / `EndTabItem()` | ❌ **NO - only if Begin returned true** |
| `BeginPopup()` / `EndPopup()` | ❌ **NO - only if Begin returned true** |
| `BeginMenu()` / `EndMenu()` | ❌ **NO - only if Begin returned true** |
### Toolset Begin/End
```cpp
@@ -582,7 +613,169 @@ struct ScopedCard {
};
```
## 8. Currently Integrated Editors
## 8. Avoiding Duplicate Rendering
### Overview
Duplicate rendering occurs when the same UI content is drawn multiple times per frame. This wastes GPU resources and can cause visual glitches, flickering, or assertion errors in ImGui.
### Common Causes
1. **Calling draw functions from multiple places**
2. **Forgetting to check visibility flags**
3. **Shared functions called by different cards**
4. **Rendering in callbacks that fire every frame**
### Pattern 1: Shared Draw Functions
When multiple cards need similar content, don't call the same draw function from multiple places:
```cpp
// ❌ WRONG: DrawMetadata called twice when both cards are visible
void DrawCardA() {
gui::EditorCard card("Card A", ICON_MD_A);
if (card.Begin()) {
DrawCanvas();
DrawMetadata(); // Called here...
}
card.End();
}
void DrawCardB() {
gui::EditorCard card("Card B", ICON_MD_B);
if (card.Begin()) {
DrawMetadata(); // ...AND here! Duplicate!
DrawCanvas();
}
card.End();
}
// ✅ CORRECT: Each card has its own content
void DrawCardA() {
gui::EditorCard card("Card A", ICON_MD_A);
if (card.Begin()) {
DrawCanvas();
// Card A specific content only
}
card.End();
}
void DrawCardB() {
gui::EditorCard card("Card B", ICON_MD_B);
if (card.Begin()) {
DrawMetadata(); // Card B specific content only
}
card.End();
}
```
### Pattern 2: Nested Function Calls
Watch out for functions that call other functions with overlapping content:
```cpp
// ❌ WRONG: DrawSpriteCanvas calls DrawMetadata, then DrawCustomSprites
// also calls DrawMetadata AND DrawSpriteCanvas
void DrawSpriteCanvas() {
// ... canvas code ...
DrawAnimationFrames();
DrawCustomSpritesMetadata(); // BUG: This shouldn't be here!
}
void DrawCustomSprites() {
if (BeginTable(...)) {
TableNextColumn();
DrawCustomSpritesMetadata(); // First call
TableNextColumn();
DrawSpriteCanvas(); // Calls DrawCustomSpritesMetadata again!
EndTable();
}
}
// ✅ CORRECT: Each function has clear, non-overlapping responsibilities
void DrawSpriteCanvas() {
// ... canvas code ...
DrawAnimationFrames();
// NO DrawCustomSpritesMetadata here!
}
void DrawCustomSprites() {
if (BeginTable(...)) {
TableNextColumn();
DrawCustomSpritesMetadata(); // Only place it's called
TableNextColumn();
DrawSpriteCanvas(); // Just draws the canvas
EndTable();
}
}
```
### Pattern 3: Expensive Per-Frame Operations
Don't call expensive rendering operations every frame unless necessary:
```cpp
// ❌ WRONG: RenderRoomGraphics called every frame when card is visible
void DrawRoomGraphicsCard() {
if (graphics_card.Begin()) {
auto& room = rooms_[current_room_id_];
room.RenderRoomGraphics(); // Expensive! Called every frame!
DrawRoomGfxCanvas();
}
graphics_card.End();
}
// ✅ CORRECT: Only render when room data changes
void DrawRoomGraphicsCard() {
if (graphics_card.Begin()) {
auto& room = rooms_[current_room_id_];
// RenderRoomGraphics is called in DrawRoomTab when room loads
// or when data changes - NOT every frame here
DrawRoomGfxCanvas(); // Just displays already-rendered data
}
graphics_card.End();
}
```
### Pattern 4: Visibility Flag Checks
Always check visibility before drawing:
```cpp
// ❌ WRONG: Card drawn without visibility check
void Update() {
DrawMyCard(); // Always called!
}
// ✅ CORRECT: Check visibility first
void Update() {
if (show_my_card_) {
DrawMyCard();
}
}
```
### Debugging Duplicate Rendering
1. **Add logging to draw functions:**
```cpp
void DrawMyContent() {
LOG_DEBUG("UI", "DrawMyContent called"); // Count calls per frame
// ...
}
```
2. **Check for multiple card instances:**
```cpp
// Search for multiple cards with similar names
grep -n "EditorCard.*MyCard" src/app/editor/
```
3. **Trace call hierarchy:**
- Use a debugger or add call stack logging
- Look for functions that call each other unexpectedly
## 9. Currently Integrated Editors
The card system is integrated across 11 of 13 editors:
@@ -604,7 +797,7 @@ The card system is integrated across 11 of 13 editors:
- **SettingsEditor** - Monolithic settings window, low usage frequency
- **AgentEditor** - Complex AI agent UI, under active development
## 9. Layout Helpers
## 10. Layout Helpers
### Overview
@@ -691,7 +884,7 @@ if (ImGui::BeginTable("##Grid", 2, ImGuiTableFlags_SizingStretchSame)) {
}
```
## 10. Workspace Management
## 11. Workspace Management
The workspace manager provides comprehensive window and layout operations:
@@ -712,7 +905,7 @@ workspace_manager_.ExecuteWorkspaceCommand(command_id);
// Supports: w.s (show all), w.h (hide all), l.s (save layout), etc.
```
## 11. Future Editor Improvements
## 12. Future Editor Improvements
This section outlines remaining improvements for editors not yet fully integrated.
@@ -734,7 +927,7 @@ This section outlines remaining improvements for editors not yet fully integrate
2. Integrate with EditorCardManager
3. Add keyboard shortcuts for common operations
## 12. Migration Checklist
## 13. Migration Checklist
Use this checklist when converting an editor to the card-based architecture:
@@ -811,7 +1004,7 @@ Use this checklist when converting an editor to the card-based architecture:
- [ ] Add example to this guide if pattern is novel
- [ ] Update CLAUDE.md if editor behavior changed significantly
## 13. Code Examples
## 14. Code Examples
### Complete Editor Implementation
@@ -1093,7 +1286,7 @@ void MyEditor::DrawPropertiesCard() {
} // namespace yaze
```
## 14. Common Pitfalls
## 15. Common Pitfalls
### 1. Forgetting Bidirectional Visibility Sync
@@ -1197,6 +1390,113 @@ if (card.Begin()) {
card.End(); // ALWAYS called
```
### 5a. BeginChild/EndChild Mismatch (Most Common Bug!)
**Problem:** `EndTable() call should only be done while in BeginTable() scope` assertion, or other strange ImGui crashes.
**Cause:** `EndChild()` placed inside the if block instead of outside.
**Why it's confusing:** Unlike `BeginTable()`, the `BeginChild()` function requires `EndChild()` to be called regardless of the return value. Many developers assume all Begin/End pairs work the same way.
**Solution:**
```cpp
// ❌ WRONG - EndChild inside if block
void DrawList() {
if (ImGui::BeginChild("##List", ImVec2(0, 0), true)) {
for (int i = 0; i < items.size(); i++) {
ImGui::Selectable(items[i].c_str());
}
ImGui::EndChild(); // BUG! Not called when BeginChild returns false!
}
}
// ✅ CORRECT - EndChild outside if block
void DrawList() {
if (ImGui::BeginChild("##List", ImVec2(0, 0), true)) {
for (int i = 0; i < items.size(); i++) {
ImGui::Selectable(items[i].c_str());
}
}
ImGui::EndChild(); // ALWAYS called!
}
```
**Files where this bug was found and fixed:**
- `sprite_editor.cc` - `DrawSpriteCanvas()`, `DrawSpritesList()`
- `dungeon_editor_v2.cc` - `DrawRoomsListCard()`, `DrawEntrancesListCard()`
- `assembly_editor.cc` - `DrawCurrentFolder()`
- `object_editor_card.cc` - `DrawTemplatesTab()`
### 5b. Duplicate Rendering in Shared Functions
**Problem:** UI elements appear twice, performance degradation, visual glitches.
**Cause:** A draw function is called from multiple places, or a function calls another function that draws the same content.
**Example of the bug:**
```cpp
// DrawSpriteCanvas was calling DrawCustomSpritesMetadata
// DrawCustomSprites was also calling DrawCustomSpritesMetadata AND DrawSpriteCanvas
// Result: DrawCustomSpritesMetadata rendered twice!
void DrawSpriteCanvas() {
// ... canvas drawing ...
DrawAnimationFrames();
DrawCustomSpritesMetadata(); // ❌ BUG: Also called by DrawCustomSprites!
}
void DrawCustomSprites() {
TableNextColumn();
DrawCustomSpritesMetadata(); // First call
TableNextColumn();
DrawSpriteCanvas(); // ❌ Calls DrawCustomSpritesMetadata AGAIN!
}
```
**Solution:** Each function should have clear, non-overlapping responsibilities:
```cpp
void DrawSpriteCanvas() {
// ... canvas drawing ...
DrawAnimationFrames();
// NO DrawCustomSpritesMetadata here - it belongs in DrawCustomSprites only
}
void DrawCustomSprites() {
TableNextColumn();
DrawCustomSpritesMetadata(); // Only place it's called
TableNextColumn();
DrawSpriteCanvas(); // Just draws canvas + animations
}
```
### 5c. Expensive Operations Called Every Frame
**Problem:** Low FPS, high CPU usage when certain cards are visible.
**Cause:** Expensive operations like `RenderRoomGraphics()` called unconditionally every frame.
**Solution:**
```cpp
// ❌ WRONG - Renders every frame
void DrawRoomGraphicsCard() {
if (graphics_card.Begin()) {
room.RenderRoomGraphics(); // Expensive! Called 60x per second!
DrawCanvas();
}
graphics_card.End();
}
// ✅ CORRECT - Only render when needed
void DrawRoomGraphicsCard() {
if (graphics_card.Begin()) {
// RenderRoomGraphics is called in DrawRoomTab when room loads,
// or when room data changes - NOT every frame
DrawCanvas(); // Just displays already-rendered data
}
graphics_card.End();
}
```
### 6. Not Testing Minimize-to-Icon
**Problem:** Control panel can't be reopened after minimizing.
@@ -1329,4 +1629,4 @@ For questions or suggestions about GUI consistency, please open an issue on GitH
---
**Last Updated**: October 13, 2025
**Last Updated**: November 26, 2025

View File

@@ -52,14 +52,85 @@ struct PaletteGroupMap {
#### Structure
- **20 dungeon palettes** in the `dungeon_main` group
- **90 colors per palette** (full SNES palette for BG layers)
- **ROM Location**: `kDungeonMainPalettes` (check `snes_palette.cc` for exact address)
- **180 bytes per palette** (90 colors × 2 bytes per color)
- **ROM Location**: `kDungeonMainPalettes = 0xDD734`
#### Usage
#### Palette Lookup System (CRITICAL)
**IMPORTANT**: Room headers store a "palette set ID" (0-71), NOT a direct palette index!
The game uses a **two-level lookup system** to convert room palette properties to actual
dungeon palette indices:
1. **Palette Set Table** (`paletteset_ids` at ROM `0x75460`)
- 72 entries, each 4 bytes: `[bg_palette_offset, aux1, aux2, aux3]`
- The first byte is a **byte offset** into the palette pointer table
2. **Palette Pointer Table** (ROM `0xDEC4B`)
- Contains 16-bit words that, when divided by 180, give the palette index
- Each word = ROM offset into dungeon palette data
**Correct Lookup Algorithm**:
```cpp
// Loading a dungeon palette
constexpr uint32_t kPalettesetIds = 0x75460;
constexpr uint32_t kDungeonPalettePointerTable = 0xDEC4B;
// room.palette is 0-71 (palette set ID, NOT palette index!)
uint8_t byte_offset = paletteset_ids[room.palette][0]; // Step 1
uint16_t word = rom.ReadWord(kDungeonPalettePointerTable + byte_offset); // Step 2
int palette_id = word / 180; // Step 3: convert ROM offset to palette index
```
**Example Lookup**:
```
Room palette property = 16
→ paletteset_ids[16][0] = 0x10 (byte offset 16)
→ Word at 0xDEC4B + 16 = 0x05A0 (1440)
→ Palette ID = 1440 / 180 = 8
→ Use dungeon_main[8], NOT dungeon_main[16]!
```
**The Pointer Table (0xDEC4B)**:
| Offset | Word | Palette ID |
|--------|--------|------------|
| 0 | 0x0000 | 0 |
| 2 | 0x00B4 | 1 |
| 4 | 0x0168 | 2 |
| 6 | 0x021C | 3 |
| ... | ... | ... |
| 38 | 0x0D5C | 19 |
#### Common Pitfall: Direct Palette ID Usage
**WRONG** (causes purple/wrong colors for palette sets 16+):
```cpp
// BUG: Uses byte offset directly as palette ID!
palette_id = paletteset_ids[room.palette][0];
```
**CORRECT**:
```cpp
auto offset = paletteset_ids[room.palette][0];
auto word = rom->ReadWord(0xDEC4B + offset);
palette_id = word.value() / 180;
```
#### Standard Usage
```cpp
// Loading a dungeon palette (with proper lookup)
auto& dungeon_pal_group = rom->palette_group().dungeon_main;
int num_palettes = dungeon_pal_group.size(); // Should be 20
int palette_id = room.palette; // Room's palette ID (0-19)
// Perform the two-level lookup
constexpr uint32_t kDungeonPalettePointerTable = 0xDEC4B;
int palette_id = room.palette; // Default fallback
if (room.palette < paletteset_ids.size()) {
auto offset = paletteset_ids[room.palette][0];
auto word = rom->ReadWord(kDungeonPalettePointerTable + offset);
if (word.ok()) {
palette_id = word.value() / 180;
}
}
// IMPORTANT: Use operator[] not palette() method!
auto palette = dungeon_pal_group[palette_id]; // Returns reference
@@ -302,6 +373,10 @@ constexpr uint32_t kDungeonMainPalettes = 0xDD734;
constexpr uint32_t kHardcodedGrassLW = 0x5FEA9;
constexpr uint32_t kTriforcePalette = 0xF4CD0;
constexpr uint32_t kOverworldMiniMapPalettes = 0x55B27;
// Dungeon palette lookup tables (critical for room rendering!)
constexpr uint32_t kPalettesetIds = 0x75460; // 72 entries × 4 bytes
constexpr uint32_t kDungeonPalettePointerTable = 0xDEC4B; // Palette ROM offsets
```
## Graphics Sheet Palette Application
@@ -351,3 +426,103 @@ bitmap.mutable_data() = new_data;
// CORRECT - Updates both vector and surface
bitmap.set_data(new_data);
```
## Bitmap Dual Palette System
### Understanding the Two Palette Storage Mechanisms
The `Bitmap` class has **two separate palette storage locations**, which can cause confusion:
| Storage | Location | Populated By | Used For |
|---------|----------|--------------|----------|
| Internal SnesPalette | `bitmap.palette_` | `SetPalette(SnesPalette)` | Serialization, palette editing |
| SDL Surface Palette | `surface_->format->palette` | Both `SetPalette` overloads | Actual rendering to textures |
### The Problem: Empty palette() Returns
When dungeon rooms apply palettes to their layer buffers, they use `SetPalette(vector<SDL_Color>)`:
```cpp
// In room.cc - CreateAllGraphicsLayers()
auto set_dungeon_palette = [](gfx::Bitmap& bmp, const gfx::SnesPalette& pal) {
std::vector<SDL_Color> colors(256);
for (size_t i = 0; i < pal.size() && i < 256; ++i) {
ImVec4 rgb = pal[i].rgb();
colors[i] = { static_cast<Uint8>(rgb.x), static_cast<Uint8>(rgb.y),
static_cast<Uint8>(rgb.z), 255 };
}
colors[255] = {0, 0, 0, 0}; // Transparent
bmp.SetPalette(colors); // Uses SDL_Color overload!
};
```
This means `bitmap.palette().size()` returns **0** even though the bitmap renders correctly!
### Solution: Extract Palette from SDL Surface
When you need to copy a palette between bitmaps (e.g., for layer compositing), extract it from the SDL surface:
```cpp
void CopyPaletteBetweenBitmaps(const gfx::Bitmap& src, gfx::Bitmap& dst) {
SDL_Surface* src_surface = src.surface();
if (!src_surface || !src_surface->format) return;
SDL_Palette* src_pal = src_surface->format->palette;
if (!src_pal || src_pal->ncolors == 0) return;
// Extract palette colors into a vector
std::vector<SDL_Color> colors(256);
int colors_to_copy = std::min(src_pal->ncolors, 256);
for (int i = 0; i < colors_to_copy; ++i) {
colors[i] = src_pal->colors[i];
}
// Apply to destination bitmap
dst.SetPalette(colors);
}
```
### Layer Compositing with Correct Palettes
When merging multiple layers into a single composite bitmap (as done in `RoomLayerManager::CompositeToOutput()`), the correct approach is:
1. Create/clear the output bitmap
2. For each visible layer:
- Extract the SDL palette from the first layer with a valid surface
- Apply it to the output bitmap using `SetPalette(vector<SDL_Color>)`
- Composite the pixel data (skip transparent indices 0 and 255)
3. Sync pixel data to surface with `UpdateSurfacePixels()`
4. Mark as modified for texture update
**Example from RoomLayerManager**:
```cpp
void RoomLayerManager::CompositeToOutput(Room& room, gfx::Bitmap& output) const {
// Create output bitmap
output.Create(512, 512, 8, std::vector<uint8_t>(512*512, 255));
bool palette_copied = false;
for (auto layer_type : GetDrawOrder()) {
auto& buffer = GetLayerBuffer(room, layer_type);
const auto& src_bitmap = buffer.bitmap();
// Copy palette from first visible layer
if (!palette_copied && src_bitmap.surface()) {
ApplySDLPaletteToBitmap(src_bitmap.surface(), output);
palette_copied = true;
}
// Composite pixels...
}
output.UpdateSurfacePixels();
output.set_modified(true);
}
```
### Best Practices for Palette Handling
1. **Don't assume palette() has data**: Always check `palette().size() > 0` before using it
2. **Use SDL surface as authoritative source**: For rendering-related palette operations
3. **Use SetPalette(SnesPalette) for persistence**: When the palette needs to be saved or edited
4. **Use SetPalette(vector<SDL_Color>) for performance**: When you already have SDL colors
5. **Always call UpdateSurfacePixels()**: After modifying pixel data and before rendering

View File

@@ -1,14 +1,16 @@
# A1 - Testing Guide
# Testing Guide
This guide provides a comprehensive overview of the testing framework for the yaze project, including the test organization, execution methods, and the end-to-end GUI automation system.
This guide covers the testing framework for YAZE, including test organization, execution, and the GUI automation system.
## 1. Test Organization
---
The test suite is organized into a clear directory structure that separates tests by their purpose and dependencies. This is the primary way to understand the nature of a test.
## Test Organization
Tests are organized by purpose and dependencies:
```
test/
├── unit/ # Unit tests for individual components
├── unit/ # Isolated component tests
│ ├── core/ # Core functionality (asar, hex utils)
│ ├── cli/ # Command-line interface tests
│ ├── emu/ # Emulator component tests
@@ -16,138 +18,132 @@ test/
│ ├── gui/ # GUI widget tests
│ ├── rom/ # ROM data structure tests
│ └── zelda3/ # Game-specific logic tests
├── integration/ # Tests for interactions between components
│ ├── ai/ # AI agent and vision tests
├── integration/ # Component interaction tests
│ ├── ai/ # AI agent tests
│ ├── editor/ # Editor integration tests
│ └── zelda3/ # Game-specific integration tests (ROM-dependent)
├── e2e/ # End-to-end user workflow tests (GUI-driven)
│ └── zelda3/ # ROM-dependent integration tests
├── e2e/ # End-to-end GUI workflow tests
│ ├── rom_dependent/ # E2E tests requiring a ROM
│ └── zscustomoverworld/ # ZSCustomOverworld upgrade E2E tests
│ └── zscustomoverworld/ # ZSCustomOverworld upgrade tests
├── benchmarks/ # Performance benchmarks
├── mocks/ # Mock objects for isolating tests
└── assets/ # Test assets (patches, data)
├── mocks/ # Mock objects
└── assets/ # Test data and patches
```
## 2. Test Categories
---
Based on the directory structure, tests fall into the following categories:
## Test Categories
### Unit Tests (`unit/`)
- **Purpose**: To test individual classes or functions in isolation.
- **Characteristics**:
- Fast, self-contained, and reliable.
- No external dependencies (e.g., ROM files, running GUI).
- Form the core of the CI/CD validation pipeline.
| Category | Purpose | Dependencies | Speed |
|----------|---------|--------------|-------|
| **Unit** | Test individual classes/functions | None | Fast |
| **Integration** | Test component interactions | May require ROM | Medium |
| **E2E** | Simulate user workflows | GUI + ROM | Slow |
| **Benchmarks** | Measure performance | None | Variable |
### Integration Tests (`integration/`)
- **Purpose**: To verify that different components of the application work together correctly.
- **Characteristics**:
- May require a real ROM file (especially those in `integration/zelda3/`). These are considered "ROM-dependent".
- Test interactions between modules, such as the `asar` wrapper and the `Rom` class, or AI services with the GUI controller.
- Slower than unit tests but crucial for catching bugs at module boundaries.
### Unit Tests
### End-to-End (E2E) Tests (`e2e/`)
- **Purpose**: To simulate a full user workflow from start to finish.
- **Characteristics**:
- Driven by the **ImGui Test Engine**.
- Almost always require a running GUI and often a real ROM.
- The slowest but most comprehensive tests, validating the user experience.
- Includes smoke tests, canvas interactions, and complex workflows like ZSCustomOverworld upgrades.
Fast, isolated tests with no external dependencies. Run in CI on every commit.
### Benchmarks (`benchmarks/`)
- **Purpose**: To measure and track the performance of critical code paths, particularly in the graphics system.
- **Characteristics**:
- Not focused on correctness but on speed and efficiency.
- Run manually or in specialized CI jobs to prevent performance regressions.
### Integration Tests
## 3. Running Tests
Test module interactions (e.g., `asar` wrapper with `Rom` class). Some require a ROM file.
> 💡 Need a refresher on presets/commands? See the [Build & Test Quick Reference](../build/quick-reference.md)
> for the canonical `cmake`, `ctest`, and helper script usage before running the commands below.
### E2E Tests
### Using the Enhanced Test Runner (`yaze_test`)
GUI-driven tests using ImGui Test Engine. Validate complete user workflows.
The most flexible way to run tests is by using the `yaze_test` executable directly. It provides flags to filter tests by category, which is ideal for development and AI agent workflows.
### Benchmarks
Performance measurement for critical paths. Run manually or in specialized CI jobs.
---
## Running Tests
> See the [Build and Test Quick Reference](../build/quick-reference.md) for full command reference.
### Using yaze_test Executable
```bash
# First, build the test executable
cmake --build build_ai --target yaze_test
# Build
cmake --build build --target yaze_test
# Run all tests
./build_ai/bin/yaze_test
./build/bin/yaze_test
# Run only unit tests
./build_ai/bin/yaze_test --unit
# Run by category
./build/bin/yaze_test --unit
./build/bin/yaze_test --integration
./build/bin/yaze_test --e2e --show-gui
# Run only integration tests
./build_ai/bin/yaze_test --integration
# Run ROM-dependent tests
./build/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
# Run E2E tests (requires a GUI)
./build_ai/bin/yaze_test --e2e --show-gui
# Run by pattern
./build/bin/yaze_test "*Asar*"
# Run ROM-dependent tests with a specific ROM
./build_ai/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
# Run tests matching a specific pattern (e.g., all Asar tests)
./build_ai/bin/yaze_test "*Asar*"
# Get a full list of options
./build_ai/bin/yaze_test --help
# Show all options
./build/bin/yaze_test --help
```
### Using CTest and CMake Presets
For CI/CD or a more traditional workflow, you can use `ctest` with CMake presets.
### Using CTest
```bash
# Configure a development build (enables ROM-dependent tests)
cmake --preset mac-dev -DYAZE_TEST_ROM_PATH=/path/to/your/zelda3.sfc
# Configure with ROM tests
cmake --preset mac-dev -DYAZE_TEST_ROM_PATH=/path/to/zelda3.sfc
# Build the tests
# Build
cmake --build --preset mac-dev --target yaze_test
# Run stable tests (fast, primarily unit tests)
ctest --preset dev
# Run all tests, including ROM-dependent and E2E
ctest --preset all
# Run tests
ctest --preset dev # Stable tests
ctest --preset all # All tests
```
## 4. Writing Tests
---
When adding new tests, place them in the appropriate directory based on their purpose and dependencies.
## Writing Tests
- **New class `MyClass`?** Add `test/unit/my_class_test.cc`.
- **Testing `MyClass` with a real ROM?** Add `test/integration/my_class_rom_test.cc`.
- **Testing a full UI workflow involving `MyClass`?** Add `test/e2e/my_class_workflow_test.cc`.
Place tests based on their purpose:
## 5. E2E GUI Testing Framework
| Test Type | File Location |
|-----------|---------------|
| Unit test for `MyClass` | `test/unit/my_class_test.cc` |
| Integration with ROM | `test/integration/my_class_rom_test.cc` |
| UI workflow test | `test/e2e/my_class_workflow_test.cc` |
The E2E framework uses `ImGuiTestEngine` to automate UI interactions.
---
### Architecture
## E2E GUI Testing
- **`test/yaze_test.cc`**: The main test runner that can initialize a GUI for E2E tests.
- **`test/e2e/`**: Contains all E2E test files, such as:
- `framework_smoke_test.cc`: Basic infrastructure verification.
- `canvas_selection_test.cc`: Canvas interaction tests.
- `dungeon_editor_tests.cc`: UI tests for the dungeon editor.
- **`test/test_utils.h`**: Provides high-level helper functions for common actions like loading a ROM (`LoadRomInTest`) or opening an editor (`OpenEditorInTest`).
The E2E framework uses ImGui Test Engine for UI automation.
### Key Files
| File | Purpose |
|------|---------|
| `test/yaze_test.cc` | Main test runner with GUI initialization |
| `test/e2e/framework_smoke_test.cc` | Infrastructure verification |
| `test/e2e/canvas_selection_test.cc` | Canvas interaction tests |
| `test/e2e/dungeon_editor_tests.cc` | Dungeon editor UI tests |
| `test/test_utils.h` | Helper functions (LoadRomInTest, OpenEditorInTest) |
### Running GUI Tests
To run E2E tests and see the GUI interactions, use the `--show-gui` flag.
```bash
# Run all E2E tests with the GUI visible
./build_ai/bin/yaze_test --e2e --show-gui
# Run all E2E tests with visible GUI
./build/bin/yaze_test --e2e --show-gui
# Run a specific E2E test by name
./build_ai/bin/yaze_test --show-gui --gtest_filter="*DungeonEditorSmokeTest"
# Run specific test
./build/bin/yaze_test --show-gui --gtest_filter="*DungeonEditorSmokeTest"
```
### Widget Discovery and AI Integration
### AI Integration
The GUI testing framework is designed for AI agent automation. All major UI elements are registered with stable IDs, allowing an agent to "discover" and interact with them programmatically via the `z3ed` CLI.
UI elements are registered with stable IDs for programmatic access via `z3ed`:
- `z3ed gui discover` - List available widgets
- `z3ed gui click` - Interact with widgets
- `z3ed agent test replay` - Replay recorded tests
Refer to the `z3ed` agent guide for details on using commands like `z3ed gui discover`, `z3ed gui click`, and `z3ed agent test replay`.
See the [z3ed CLI Guide](../usage/z3ed-cli.md) for more details.

View File

@@ -300,7 +300,7 @@ After pushing, CI will run tests on all platforms (Linux, macOS, Windows):
scripts/agents/run-gh-workflow.sh ci.yml -f enable_http_api_tests=true
```
See [GH Actions Remote Guide](../../internal/agents/gh-actions-remote.md) for setup.
See [GH Actions Remote Guide](../../internal/agents/archive/utility-tools/gh-actions-remote.md) for setup.
## Advanced Topics

View File

@@ -151,8 +151,8 @@ jobs:
- name: Build z3ed
run: |
cmake -B build_test
cmake --build build_test --parallel
cmake -B build
cmake --build build --parallel
- name: Run Agent Tests (Mock ROM)
run: |