Lessons from Outside 2. Spec-in, code-out — generators against the seam
Next →
Chapter · Lessons from Outside

2. Spec-in, code-out — generators against the seam

A whole class of FRC tools takes a specification and hands you back robot code — and every one of them optimizes the wrong axis for this book. Lesson 1 named the pattern in a paragraph and left a rule hanging: generate the constants, own the architecture. This chapter is the full treatment. It walks the generator landscape tool by tool, shows why time-to-driving-robot and seam quality are close to orthogonal, and turns that rule from a slogan into a build recipe. It does not cover path and trajectory generators (PathPlanner, Choreo, the retired PathWeaver) — those emit motion, not robot code, and belong with the autonomy material.

The one axis that sorts the landscape

Every tool below answers two questions, and the answers correlate. Who owns the drivetrain code after generation — you, or a black-box library you depend on? And where do the vendor SDK types live — below the IO seam you control, smeared across your subsystems, or hidden inside the library? The rubric’s golden rule is that com.ctre.* / com.revrobotics.* / org.photonvision.* must not appear above the seam, and that subsystems must be unit-testable in sim. Measured against that, the generators land like this:

ToolSpec formatRound-trip modelVendor couplingYou own the code?Rubric ceiling
WPILib RobotBuilderGUI subsystem/command treeProtected regions — true round-tripConcrete HW classes in subsystems; no seamYes (skeleton)Low — deprecated, removed 2027
CTRE Phoenix Tuner X swerve generatorWizard + characterizationOne-shot, no markersMaximalcom.ctre.phoenix6 is the drivetrain; all-CTRE HWYes (emitted file)High performance, zero seam — locked to CTRE
YAGSLJSON config directoryBlack-box library (vendordep)Hidden inside the lib; version-coupledNo — only a thin wrapperMid — can’t trace/extend, replay hard
AI / LLMNatural-language promptN/AWhatever it hallucinates — often deprecated APIsYesLow — no traction as of 2026

RobotBuilder — the original, now dying

RobotBuilder is the WPILib GUI tool: you decompose the robot into a tree of subsystems and commands, set ports and PID gains, bind commands to buttons, and it emits a Java/C++/Python skeleton — declarations, button mappings, default-command wiring — that you finish by hand. Its round-trip model is genuinely the most sophisticated of anything here: generated code lives between // BEGIN AUTOGENERATED CODE / // END AUTOGENERATED CODE markers, regenerates inside them, and preserves your edits outside them.

The architecture it produces is exactly the anti-pattern this book exists to flag. Generated subsystems reference concrete hardware classes directly — PWMVictorSPX, AnalogPotentiometer — and the generated PIDSubsystem reads sensors and drives motors inside the subsystem. There is no swappable IO layer, nothing isolated for a sim test; the subsystem owns its hardware. The leaking types are mostly WPILib framework classes rather than com.ctre.*, but the structure is the one the seam replaces. And the headline fact settles the argument: RobotBuilder is officially deprecated and scheduled for removal in 2027 due to declining usage. The official toolchain is retiring the GUI-skeleton approach outright — a strong signal about where the idea leads.

Tuner X — the one with real traction, and maximum coupling

CTRE’s Phoenix Tuner X swerve project generator is the tool teams actually reach for, and it is genuinely good at what it does. You set device IDs and characterize the modules inside a wizard — encoder inverts, drivetrain inverts, a verification routine — then click generate and get a complete FRC project: a TunerConstants.java full of measured device IDs, CANcoder offsets, slip-current limits, and Slot0 gains, plus a CommandSwerveDrivetrain and a createDrivetrain() factory. It ships built-in simulation, uses FOC automatically, and produces a drivable robot in a few hours — ten minutes if firmware and IDs are already set. Community consensus favors it for all-CTRE teams, and few teams can sustainably maintain a hand-rolled equivalent during build season. Even YAGSL recommends it for all-CTRE hardware and warns you when you run YAGSL on an all-CTRE config.

The cost is total, by construction. The generated subsystem extends com.ctre.phoenix6.swerve.SwerveDrivetrain, parameterized on TalonFX, CANcoder, and Pigeon2, and the parent class constructs the hardware devices itself. It imports com.ctre.phoenix6.{configs,hardware,signals,swerve} extensively; it is built on Phoenix’s own proprietary swerve API, not WPILib’s template. There is no seam — the entire drivetrain is the vendor SDK, sitting above any IO line, requiring an all-CTRE stack: eight Talon FX motors, CTRE steer feedback, a Pigeon 2. You cannot swap in a REV motor without abandoning the generated architecture. The generator buys a high performance ceiling and a low time floor at the exact cost of the D1 seam; a team that stops at the emitted output scores well on “does it drive” and poorly on “could you swap the vendor, replay a match, or unit-test the module.” (REV’s MAXSwerve template does the same for the REV stack; in the corpus it surfaced mainly as a config preset bundled inside YAGSL, not a standalone code emitter.)

YAGSL — a seam you don’t own

YAGSL inverts the model: you don’t get emitted source at all. You author a JSON spec under src/main/deploy/swerve/swervedrive.json, per-corner module files, physical and PID properties — and a single SwerveParser(dir).createSwerveDrive(...) call instantiates the entire drivetrain at runtime. You write and own only a thin SwerveSubsystem wrapper and your commands; the drivetrain implementation lives inside the library. Its real advantage over CTRE’s generator is multi-vendor reach: motor controllers are selected by a JSON type string across three vendor families — REV, CTRE, and Thrifty Nova — abstracted behind an internal SwerveMotor wrapper, with a Flutter desktop configurator to build and validate the configs.

That wrapper is where the subtlety lives. YAGSL does abstract vendors, and the author frames the SwerveMotor wrapper as “the IO layer”; it ships sim objects, maple-sim support, and AdvantageScope-compatible logging. But it is the library’s seam, not yours. You cannot code-trace or extend the per-calculation behavior, which is precisely why experienced teams decline it — “you lose control over how the swerve calculates,” which hurts both teaching and on-field diagnosis. Concretely: AdvantageKit replay is famously hard on YAGSL — AK requires modifying user code, which conflicts with YAGSL’s self-contained design, so bolting on record-and-replay means forking the library; it uses plain VelocityVoltage with no FOC; and it is version-coupled, a pinned vendordep whose configs break across upgrades so you migrate the JSON, not your code. YAGSL wins where it is designed to: it produces a functioning swerve with little prior knowledge, which makes it the right call for a team with no returning programmers, and broad adoption means easy troubleshooting. The honest forum framing is that swerve libraries differ little in lap time because none model the hard dynamics — so the choice is about ownership and learning, not performance.

AI and LLM generators — no prompt-to-robot tool, yet

The skeptical verdict is clear: as of the 2026 season there is no established prompt-to-robot-code generator with real adoption in FRC. The structural reason is that vendor APIs change every year and the old training data dwarfs the new, so LLMs emit outdated code. Asked for a tank drive, GPT-5-mini produced a hybrid using MotorControllerGroup — deprecated since 2024 — and didn’t actually use commands despite extending SubsystemBase; prompts for “PathPlanner + AdvantageKit + CTRE swerve” did not yield working code. What teams actually use AI for, per a 112-voter poll, is debugging and boilerplate subsystems and constants; only a handful used it for anything like swerve kinematics, and a substantial fraction reported an explicit “No AI” policy. It is enhanced autocomplete for repetitive IO classes, not an architecture generator — and it only accelerates people who already read and understand the output. The bounding datapoints run from a vibe-coded driver-station component that failed at its first competition and got the team’s AI banned, to one frontier contributor reporting 100% AI-generated-or-reviewed code on a 2026 competition robot.

The implication points straight back at this project. An LLM that actually understood the seam, the inputs-struct logging contract, and sim-backed tests is the unoccupied territory — the generators above produce vendor-coupled or hidden-seam code fast, and an architecture-aware generator is the gap. That is the thesis behind the scaffold-robot and add-subsystem skills: generate the seam, not just the wiring.

The reconciling move — generate the numbers, own the seam

Every generator here optimizes time-to-driving-robot, and that axis is close to orthogonal to seam quality — for CTRE’s generator and YAGSL, actively opposed. The CTRE generator puts vendor types above any seam; YAGSL hides the seam in a versioned black box; RobotBuilder’s subsystems own their hardware and the tool is being retired. None of them, used as shipped, lands a team above the middle of the modularity ladder — not because the code is bad, but because the seam the rubric rewards isn’t there.

The move that reconciles the two axes is to separate the two artifacts a generator conflates: the characterized constants and the architecture. The genuinely valuable output of the Tuner X wizard is TunerConstants.java — real, measured device IDs, CANcoder offsets, slip current, and Slot0 gains you’d otherwise hand-tune. Take those numbers; leave the vendor drivetrain behind. Ingest them behind a ModuleIO / GyroIO seam you own and can replay, exactly the seam the drivetrain subsystem specifies. The build-spec line is one sentence: let a generator produce the numbers; never let it produce the architecture. That single rule turns every tool in this chapter from an architectural dead-end into a legitimate accelerator, and it is the foundation-first discipline applied to the tools themselves.

Which tool a team should touch follows from where it sits. An all-CTRE team may use Tuner X — its output is fast and correct on CTRE hardware — but must wrap the constants behind its own seam rather than shipping the generated drivetrain. A multi-vendor team without strong learning goals may take YAGSL, accepting the hidden seam as the price of a working drive with little prior knowledge. A team with elite ambition hand-writes the seam and lets the generator supply only the tuned numbers.

One frontier note keeps this from being settled. 6328’s AdvantageKit swerve template already ingests the Tuner X config directly — it loads the generated constants but wraps them in a ModuleIO / GyroIO seam you own, can replay, and can extend (grafting 254’s setpoint generator, for instance). For a team already on PathPlanner, AdvantageKit IO, and PhotonVision localization, experienced programmers recommend this over switching to YAGSL, because the optimizations YAGSL hides are present in the AK template in a form you can code-trace. It is the clearest existing proof that “generate the constants, own the architecture” is not a compromise but the strictly better path — and the strongest candidate to promote into scaffold-robot as a concrete generator-to-seam adapter.