Workout Creation Tools Architecture¶
This document defines the current canonical architecture for AeroBeat workout creation tooling.
It builds on the already-locked package/content/tool lane decisions:
- authored workout truth lives in package YAML
aerobeat-content-coreowns durable content contractsaerobeat-tool-coreplus concreteaerobeat-tool-*repos own authoring, validation, migration, packaging, and import workflows- the first workout-creation product should cover the full package, while staying far away from becoming a general-purpose audio/video editor
Purpose¶
The workout creation tools product exists to let creators author, inspect, validate, repair, migrate, import, and package a complete AeroBeat workout package.
That product includes both:
- headless CLI workflows for validation, migration, packaging, CI, and automation
- interactive GUI workflows for package editing, chart authoring, and guided media import
The product goal is full package coverage, not full media-production tooling.
Locked architectural position¶
Product/repo boundary¶
The workout creation tools live in their own concrete tool repo.
- canonical repo family:
aerobeat-tool-* - this workout-creation product is separate from
aerobeat-assembly-community aerobeat-assembly-communitymay embed the tool as a user-facing mode through GodotEnv- embedded mode is a desired UX/distribution path, but ownership remains with the separate tool repo
This preserves tool release cadence, testing boundaries, and ownership clarity.
Tool-lane dependency boundary¶
The workout creation tools should:
- consume durable package/content contracts from
aerobeat-content-core - consume tool-common workflow/result contracts from
aerobeat-tool-core - expose creator-facing CLI and GUI entrypoints on top of shared workflow services
They should not redefine package schema locally, and they should not move package ownership into an assembly repo.
CLI architecture¶
Separate CLI tools early¶
The current locked direction is to use separate CLI tools early, one per YAML domain or workflow unit, rather than one large shared binary with deep subcommand trees.
Reasons:
- cleaner separation of concerns while the product is still evolving quickly
- easier targeted testing and debugging
- fewer hidden couplings between unrelated workflow surfaces
- a simpler path to domain-specific
validate,inspect, andfixbehavior
The package domains/workflow units currently in scope are:
workoutsongchartsetcoach-configenvironment
The exact executable names can evolve, but the domain split should remain explicit.
Validation boundary¶
Validation of one YAML file must not imply full-package validation.
The locked rule is:
- each domain tool validates its own authored record shape, local field rules, and directly referenced file existence as appropriate
- package-wide workout validation is orchestrated from the
workoutvalidation flow - full workout validation is responsible for walking the package graph and invoking the relevant lower-level validators for referenced songs, charts, sets, coach config, environments, and package media/layout rules
That means chart validate is not secretly equivalent to workout validate, and song validate does not imply package integrity.
Required CLI workflow categories¶
The minimum durable workflow categories remain:
validateinspectmigratepackageimportfix
Some of those may be domain-specific and some may be package-level orchestration surfaces, but the product should preserve headless capability for all of them.
CLI repair policy¶
CLI validation should hard-error when the package or record is invalid, including when the issue is potentially fixable.
The CLI should then direct the user toward:
- a broad
--fixpath for safe structural/content repair - more targeted
--fix-*commands where useful for specific repair classes
Examples of issues that should fail validation and recommend repair instead of silently mutating during read/validate:
- invalid enums
- malformed beat payloads
- duplicate ids
- illegal package layout
- fixable out-of-date schema/content structure
The important boundary is that validation reports problems; repair is an explicit follow-up action.
GUI architecture¶
One scene per YAML file¶
The canonical first-pass GUI structure is one Godot scene per YAML file/domain:
workoutsongchartsetcoach-configenvironment
This is a workflow-ownership decision, not a promise that every scene has equal complexity.
Package-home UX¶
Opening a package should land on the workout.yaml scene.
That scene is the package-home / overview surface. It should:
- present package-level metadata and health
- surface package-wide validation/repair/import/package actions
- show navigation outward to the other package editors
- act as the main entrypoint for full-package operations
This matches the package contract where workout.yaml is the package root and orders the authored sets.
Chart editor as the heavyweight specialized scene¶
The chart scene is the specialized high-complexity editor in the product.
It owns:
- audio-backed timeline/waveform scrubbing
- visual beat placement
- preview/playback
- chart creation against a chosen audio source
- future quick test entry from a selected timeline point after the normal set-start calibration path
By contrast, the other YAML scenes should mostly be simpler form editors with validation, media slotting, preview where useful, and links into related records.
Bounded GUI scope¶
The GUI should support package creation and editing without turning into a full DAW, NLE, or 3D DCC.
Explicitly in scope:
- forms for package metadata and referenced records
- drag-and-drop / file-selection import flows
- validation errors with non-technical user-facing messaging
- chart editing with timeline/beat tooling
- image preview/cropping for environment/editor media flows
- desirable media preview for video where practical
- future GLB preview plus scale/rotation/orientation controls in the environment editor
Explicitly not in scope for this product slice:
- full audio editing/mixing
- full video editing
- broad media-production pipelines unrelated to package authoring
- runtime-asset optimization missions such as KTX2/Draco conversion as baseline authoring requirements
Shared service rule¶
CLI and GUI surfaces should call the same underlying workflow services where possible.
That includes services for:
- validation
- repair/fix
- migration
- package assembly
- media import/normalization
- file replacement
- relationship/index refresh where needed
The GUI should not grow a second repair engine, and the CLI should not own private validation logic that the GUI cannot reuse.
Media import and normalization architecture¶
FFmpeg is a dependency¶
ffmpeg is a required dependency of the workout creation tools for media import/normalization workflows.
This is the canonical current direction for converting accepted source media into the stored canonical package formats.
Import flow¶
When a user imports or replaces package media, the workflow should be:
- validate the selected asset for the intended slot/purpose
- convert to canonical stored format when needed
- copy the resulting asset into the canonical package location
- rename it to the normalized schema for that slot/domain, including a uid-suffixed filename
- update the YAML reference(s) to the new canonical stored file
- remove the superseded package asset when the user replaced an existing slotted file
Example naming direction:
coaching-warm-up-video-49189afea.ogvsong-audio-49189afea.oggenvironment-background-49189afea.png
The exact naming schema can be refined later, but normalized uid-suffixed canonical filenames are locked.
Accepted formats vs stored canonical formats¶
The docs must treat these as separate concepts:
- accepted import formats: the broader set of source file types the tools may ingest
- stored canonical formats: the narrower set of formats that workout packages actually keep on disk
Those accepted import formats should be documented separately from this architecture decision doc so the system can evolve import compatibility without muddying package-storage truth.
Currently locked stored canonical formats¶
The currently locked package-storage formats are:
- song audio:
.ogg - coaching audio:
.ogg - coaching video:
.ogv - environment video:
.ogv - images:
.png - 3D environments: vanilla
.glb
These are storage decisions for the current package-authoring slice.
Explicit optimization deferrals¶
The following are explicitly deferred side-missions, not baseline workout-creation-tool scope:
- KTX2 texture optimization
- Draco-compressed GLB
- broader runtime-asset optimization work beyond the current package canonical-format decisions
That work may matter later, but it should not be smuggled into the first workout-creation-tool scope.
Image import nuance¶
For near-valid images that need packaging-friendly sizing/cropping, the preferred UX is a simple aspect-ratio marquee/crop flow before canonical conversion/storage, not immediate hard failure for every size mismatch.
Environment media note¶
Environment videos may be shorter than the set audio and loop during playback. Longer videos are acceptable because playback ends when the set transitions.
Repair / upgrade flows¶
GUI repair policy¶
When the GUI opens an invalid or out-of-date package, it should:
- clearly warn that repair may materially change the package
- recommend retesting afterward
- require explicit user confirmation before running repair
- delegate to the full package
--fixflow rather than performing silent ad hoc edits
This keeps the repair boundary honest and avoids accidental destructive mutation.
CLI repair policy¶
The CLI should not auto-repair on plain validation.
Instead:
validateshould hard-error on fixable problems- the error should recommend
--fix - targeted
--fix-*flows may exist where useful - package-wide repair orchestration belongs to the package/workout-level repair entrypoint
Editor ownership boundaries by domain¶
Workout scene¶
Owns:
- package metadata
- ordered set list
- package-wide validation, repair, migration, and packaging entrypoints
- top-level navigation to the package's other domains
Song scene¶
Owns:
- song metadata fields
- canonical song audio slot/import
- validation of song-local record fields and referenced media
Chart scene¶
Owns:
- chart timeline editing and beat placement
- playback preview and scrubbing
- future quick-test launch from selected timeline points
- chart-local validation and repair handoff
Set scene¶
Owns:
- exact package-local composition wiring
- linking
songId,chartId,environmentId, and optionalcoachingOverlayId - form-based editing of composition references
Coach-config scene¶
Owns:
- workout-level coaching enable/disable state
- roster, warm-up, cooldown, and overlay registry editing
- form-based media import/selection only
Environment scene¶
Owns:
- environment record editing
- image/video/GLB import and preview helpers appropriate to the current scope
Embedded assembly mode¶
The desired user-facing product experience includes an embedded mode inside the main build.
That means:
- the assembly can transition into the workout creation tools scenes
- the creator can navigate back out to the main game/application shell
- the tool still remains owned, versioned, and tested as a separate concrete tool repo imported via GodotEnv
Embedded availability is a UX/distribution decision, not an ownership merge.
Non-goals¶
The current workout creation tools architecture does not promise:
- one universal binary that hides all workflow boundaries
- package-wide validation from every domain validator implicitly
- silent auto-repair on open or on validate
- an integrated full audio editor
- an integrated full video editor
- an integrated full 3D content creation suite
- current-scope KTX2/Draco optimization pipelines
- assembly ownership of the tool product
Recommended implementation posture¶
Implementation should proceed in this order:
- stand up the separate tool repo with shared workflow services plus separate CLI surfaces per domain
- land package-home (
workout.yaml) orchestration flows first so package validation/repair/import boundaries stay clean - ship the lower-risk headless workflow foundations across validation, inspect, fix, migrate, import, and packaging before scene work tries to own that logic
- use Penpot to finalize the first-pass package-home and simpler form scene designs before polished GUI implementation begins
- stand up the simpler form scenes (
song,set,coach-config,environment) against the already-shipped shared services - build the heavyweight chart editor scene with waveform/timeline/preview/test-entry support
- integrate the repo into
aerobeat-assembly-communityvia GodotEnv for embedded access
That order preserves the full-package product goal while keeping repo/shared-service/headless foundations ahead of GUI work, while still treating Penpot as an important dependency for polished scene implementation.