with one click
planning
// Spec-driven planning for complex design tasks: when to plan, how to write specs as .ato files, and how to verify against requirements.
// Spec-driven planning for complex design tasks: when to plan, how to write specs as .ato files, and how to verify against requirements.
Core runtime behavior for the atopile sidebar agent: identity, persistence model, execution rules, and tool recipes.
Generate short live progress summaries for the atopile agent from recent tool events, preambles, checklist changes, and build state. Use for ephemeral UI activity text only, never for transcript replies or autonomous reasoning.
Authoritative ato authoring and review skill: language reference, stdlib, design patterns, and end-to-end board design workflow.
Frontend standards for atopile extension webviews: architecture, contracts, design system, and testing workflow.
Reference for the `.ato` declarative DSL: type system, connection semantics, constraint model, and standard library. Use when authoring or reviewing `.ato` code.
How the Faebryk component library is structured, how `_F.py` is generated, and the conventions/invariants for adding new library modules. Use when adding or modifying library components, traits, or module definitions.
| name | planning |
| description | Spec-driven planning for complex design tasks: when to plan, how to write specs as .ato files, and how to verify against requirements. |
Simple tasks โ just do it:
Complex tasks โ always plan first:
Do not ask whether to plan. For complex tasks, go straight into planning. Write the spec, create the checklist, call design_questions โ all in one turn. The user sees the spec and questions, and can steer from there. This is faster than a back-and-forth about whether to plan.
The spec and the design are one and the same .ato file. A spec is just the design at a high level of abstraction โ skeleton modules, interfaces, constraints, and requirements. As you implement, you fill in real components, pin mappings, and values. The file grows; the structure stays.
Do not create separate spec files. The main .ato file IS the spec.
Do not suffix module names with "Spec". PowerSupply, not PowerSupplySpec. These names persist into the final design โ name them for what they are, not for the fact that they started as a spec. See the ato skill ยง1.9 for naming guidance.
Every project with ICs should follow this structure. IC wrapper packages are separate from the main design.
my-project/
โโโ ato.yaml # Project-level builds defined here
โโโ main.ato # Top-level design โ imports packages, not raw parts
โโโ packages/
โ โโโ stm32g474/
โ โ โโโ stm32g474.ato # Wrapper: raw pins โ standard interfaces
โ โโโ drv8317/
โ โ โโโ drv8317.ato
โ โโโ tcan3414/
โ โโโ tcan3414.ato
โโโ parts/ # All raw parts (ICs + connectors)
โ โโโ STMicroelectronics_STM32G474CBT6/
โ โโโ TEXAS_INSTRUMENTS_DRV8317HREER/
โ โโโ Changzhou_Amass_Elec_XT30U_M/
โ โโโ ...
โโโ layouts/
| Item | Location | Why |
|---|---|---|
| IC wrapper modules | packages/<name>/<name>.ato | Complex pin mapping, reusable |
| All raw parts | parts/ (project root) | Installed by parts_install |
| Simple self-contained parts | Used directly in main.ato | No supporting components or high-level interfaces needed (e.g. connectors, LEDs, test points) |
| Generic passives | stdlib (import Resistor) | No package needed |
| Top-level design | main.ato | Imports wrappers, never raw _package |
ElectricPower, I2C, SPI, CAN, UART, SWD, USB2_0, USB2_0_IF, ElectricLogic, ElectricSignal, not raw pinsato.yaml inside package directories โ package targets are discovered automaticallyato.yaml โ use workspace_list_targets to discover package targets exposed by local packagesato.yaml formatrequires-atopile: ^0.14.0
paths:
src: ./
layout: ./layouts
builds:
default:
entry: main.ato:DualBLDCController
# Package builds โ for independent testing
stm32g474:
entry: packages/stm32g474/stm32g474.ato:STM32G474
hide_designators: true
drv8317:
entry: packages/drv8317/drv8317.ato:DRV8317
hide_designators: true
#pragma experiment("BRIDGE_CONNECT")
import ElectricPower
import CAN
import ElectricLogic
import Capacitor
from "parts/STMicroelectronics_STM32G474CBT6/STMicroelectronics_STM32G474CBT6.ato" import STMicroelectronics_STM32G474CBT6_package
module STM32G474:
"""STM32G474 MCU with decoupling and standard interfaces.
Exposes:
- power: 3.3V rail
- can: CAN FD interface (PA11/PA12)
- pwm_a: 3x PWM for motor A (TIM1: PA8/PA9/PA10)
- pwm_b: 3x PWM for motor B (TIM8: PB13/PB14/PB15)
"""
# โโ External interfaces โโ
power = new ElectricPower
can = new CAN
pwm_a = new ElectricLogic[3]
pwm_b = new ElectricLogic[3]
# โโ Package โโ
package = new STMicroelectronics_STM32G474CBT6_package
# โโ Power โโ
power.hv ~ package.VDD
power.hv ~ package.VDDA
power.lv ~ package.VSS
power.lv ~ package.VSSA
assert power.voltage within 3.3V +/- 10%
# โโ Decoupling โโ
decoupling = new Capacitor[3]
for cap in decoupling:
cap.capacitance = 100nF +/- 10%
cap.package = "C0402"
power ~> cap ~> power.lv
# โโ CAN โโ
can.tx.line ~ package.PA11
can.rx.line ~ package.PA12
can.tx.reference ~ power
can.rx.reference ~ power
# โโ PWM โโ
pwm_a[0].line ~ package.PA8
pwm_a[1].line ~ package.PA9
pwm_a[2].line ~ package.PA10
pwm_b[0].line ~ package.PB13
pwm_b[1].line ~ package.PB14
pwm_b[2].line ~ package.PB15
main.ato#pragma experiment("BRIDGE_CONNECT")
import ElectricPower
from "packages/stm32g474/stm32g474.ato" import STM32G474
from "packages/drv8317/drv8317.ato" import DRV8317
from "packages/tcan3414/tcan3414.ato" import TCAN3414
from "parts/Changzhou_Amass_Elec_XT30U_M/Changzhou_Amass_Elec_XT30U_M.ato" import Changzhou_Amass_Elec_XT30U_M_package
module DualBLDCController:
"""Dual BLDC motor controller for robot drivetrain."""
mcu = new STM32G474
motor_a = new DRV8317
motor_b = new DRV8317
can_phy = new TCAN3414
power = new ElectricPower
power ~ mcu.power
power ~ motor_a.motor_supply
power ~ motor_b.motor_supply
mcu.can ~ can_phy.can
mcu.pwm_a ~ motor_a.pwm
mcu.pwm_b ~ motor_b.pwm
The file at packages/<name>/<name>.ato is the canonical wrapper boundary for that part or subsystem.
Refine that file in place. main.ato should import those wrapper packages directly rather than routing through an extra wrapper aggregator file.
Capture natural-language requirements directly in the module's docstring under a Requirements: section. Place requirements on whichever module owns them โ top-level for system-wide requirements, on a specific subsystem for module-specific ones.
module PowerStage:
"""Three-phase MOSFET bridge sized for continuous motor current.
Requirements:
- R1: 20A continuous โ FET stage rated for 20A with thermal margin
"""
Format: - R<id>: <short text> โ <criteria>
These requirements stay in the design permanently. They document design intent alongside the implementation.
The spec is the skeleton of the design. It defines architecture, requirements, and constraints โ but leaves out implementation details (pin mappings, support circuits). Those get filled in during implementation.
#pragma experiment("BRIDGE_CONNECT")
import ElectricPower
import CAN
import ElectricLogic
module BLDCController:
"""
# BLDC Motor Controller
Dual-motor BLDC controller using STM32G474 and two DRV8317 drivers.
## Key Decisions
- STM32G474 MCU โ motor control timers + CAN FD
- DRV8317 gate driver โ 3-phase, integrated LDO
## Requirements
- R1: MCU platform โ Uses STM32G474
- R2: 5-18V input โ Operating voltage range
- R3: Dual motor โ 2x DRV8317 in 3-PWM mode
## Open Questions
- Current sensing: phase shunt vs low-side?
"""
# โโ Architecture โโ
power = new PowerSupply
control = new MCU
motor_a = new MotorDrive
motor_b = new MotorDrive
comms = new CANTransceiver
power.rail_3v3 ~ control.power
power.motor_supply ~ motor_a.supply
power.motor_supply ~ motor_b.supply
control.pwm_a ~ motor_a.pwm
control.pwm_b ~ motor_b.pwm
control.can ~ comms.can
assert power.vin.voltage within 5V to 18V
module PowerSupply:
"""Power input and regulation."""
vin = new ElectricPower
rail_3v3 = new ElectricPower
motor_supply = new ElectricPower
module MCU:
"""STM32G474 with timers and comms peripherals."""
power = new ElectricPower
can = new CAN
pwm_a = new ElectricLogic[3]
pwm_b = new ElectricLogic[3]
module MotorDrive:
"""DRV8317 3-phase gate driver."""
supply = new ElectricPower
pwm = new ElectricLogic[3]
module CANTransceiver:
"""CAN FD transceiver with UAVCAN connector.
Requirements:
- R4: CAN FD transceiver with UAVCAN connector
"""
can = new CAN
| Spec Concept | ato Mechanism |
|---|---|
| Overview | Module docstring ("""...""") |
| Architecture | Sub-modules + connections (~) |
| Requirements | - R<id>: <text> โ <criteria> in module docstring |
| Formal constraints | assert statements |
| Component selection | new instantiations |
| Sub-system descriptions | Child module docstrings |
| Open questions | Section in docstring |
When creating a spec, also create a checklist to track implementation progress. Link checklist items to spec requirements:
checklist_create({
items: [
{id: "spec", description: "Write spec and project structure", criteria: "main.ato with architecture and project-level ato.yaml"},
{id: "questions", description: "Gather design decisions", criteria: "design_questions called with all open questions"},
{id: "pkg-mcu", description: "Create MCU wrapper package", criteria: "packages/stm32g474/stm32g474.ato with standard interfaces"},
{id: "pkg-driver", description: "Create gate driver wrapper package", criteria: "packages/drv8317/drv8317.ato with standard interfaces"},
{id: "integrate", description: "Wire up top-level design", criteria: "main.ato connects packages through interfaces"},
{id: "build", description: "Build and verify", criteria: "Build passes or issues clearly identified"},
]
})
The goal is to front-load all questions and decisions, then implement without interruption.
Do steps 1-5 in a SINGLE turn โ do not end your turn after announcing you will plan.
ato.yaml and packages/ directories. Do not add manual package-wrapper build targets for generated local packages.main.ato โ architecture with sub-modules, requirements in docstrings, interface connections, and formal constraints. Use standard library interfaces (CAN, I2C, SPI, SWD, USB2_0, ElectricPower, ElectricLogic, ElectricSignal) in the spec instead of inventing local interfaces unless there is a real reusable boundary not covered by stdlib.design_questions with ALL open questions at once. Include suggested options and recommended defaults where possible โ make it easy for the user to answer quickly. Your turn ends automatically after this call.Use design_questions any time you have multiple design decisions to gather. It presents structured questions with bullet-point options that the user can answer or override with freeform text. Do not trickle questions across multiple turns โ batch them all into one design_questions call.
web_search, map pins to interfaces.
web_search pass to inspect the vendor datasheet/design guide, compare families, confirm the typical topology, and find reference-circuit guidance.main.ato or project modules above the wrapper.parts_install(project_path="packages/<name>").package_agent_spawn(project_path="packages/<name>", goal=..., comments=...) so the main agent can keep moving on integration and architecture.workspace_list_targets to discover package targets automatically exposed by local packages, then build/fix wrappers and other submodules before attempting the full design.
main.ato โ connect packages through their interfaces. No raw _package imports here.Do not end your turn to ask follow-up questions โ make reasonable assumptions and note them. The user can course-correct via steering messages.
PowerSupply stays PowerSupply.packages/, not in main.ato. Raw _package components are never imported in main.ato.