Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .agents/skills/add-ros2-package/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Define package metadata and dependencies:
</package>
```

**Reference:** `robot/ros_ws/src/local/planners/droan_local_planner/package.xml`
**Reference:** `robot/ros_ws/src/local/planners/takeoff_landing_planner/package.xml`

### 4. Create CMakeLists.txt (for C++)

Expand Down Expand Up @@ -183,7 +183,7 @@ endif()
ament_package()
```

**Reference:** `robot/ros_ws/src/local/planners/droan_local_planner/CMakeLists.txt`
**Reference:** `robot/ros_ws/src/local/planners/takeoff_landing_planner/CMakeLists.txt`

### 5. Create setup.py (for Python)

Expand Down Expand Up @@ -514,7 +514,7 @@ After creating the package:
- [Using Parameters](https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Using-Parameters-In-A-Class-CPP.html)

- **AirStack Examples:**
- Reference planner: `robot/ros_ws/src/local/planners/droan_local_planner`
- Reference planner (task server): `robot/ros_ws/src/local/planners/takeoff_landing_planner`; module-shipped local planners: asm_mighty `mighty_bridge/`, asm_droan `droan_local_planner/`
- Reference controller: `robot/ros_ws/src/local/controls/trajectory_controller`
- Package template: `assets/package_template/`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
<!-- Trajectory generation (for planners) -->
<!-- <depend>trajectory_library</depend> -->

<!-- Cost maps (for local planners) -->
<!-- Cost maps (for DROAN-style local planners; ships in the asm_droan module) -->
<!-- <depend>cost_map_interface</depend> -->

<!-- ============================================ -->
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/create-stack/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ airstack stack new full_default my_experiment # refuses overwrite
```

Pick the closest starting point: `full_default` (everything onboard),
`lite_default` (no global/logging), `full_droan_cpu`, `full_macvo`.
`lite_default` (no global/logging), `full_droan`, `full_droan_cpu`, `full_macvo`.
`stack new` deliberately does **not** copy `wiring.md` — that file is the
*source* stack's observed graph and would lie about yours.

Expand Down
11 changes: 6 additions & 5 deletions .agents/skills/debug-module/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,23 +297,24 @@ Study a working similar module:

```bash
# Find reference implementation
# Example: if debugging a planner, study droan_local_planner
# Example: if debugging a planner, study the default MIGHTY bridge
# (modules/asm_mighty/mighty_bridge after `airstack up` synced it)

# Compare:
# 1. Package structure
ls -la robot/ros_ws/src/local/planners/droan_local_planner/
ls -la modules/asm_mighty/mighty_bridge/
ls -la robot/ros_ws/src/local/planners/your_planner/

# 2. Topic structure
docker exec airstack-robot-desktop-1 bash -c "ros2 node info /robot/droan/droan_planner"
docker exec airstack-robot-desktop-1 bash -c "ros2 node info /robot/mighty/mighty_bridge"
docker exec airstack-robot-desktop-1 bash -c "ros2 node info /robot/your_planner/your_node"

# 3. Message timing
docker exec airstack-robot-desktop-1 bash -c "ros2 topic hz /robot/droan/output"
docker exec airstack-robot-desktop-1 bash -c "ros2 topic hz /robot/trajectory_controller/trajectory_override"
docker exec airstack-robot-desktop-1 bash -c "ros2 topic hz /robot/your_planner/output"

# 4. Data ranges
docker exec airstack-robot-desktop-1 bash -c "ros2 topic echo /robot/droan/output --once"
docker exec airstack-robot-desktop-1 bash -c "ros2 topic echo /robot/trajectory_controller/trajectory_override --once"
docker exec airstack-robot-desktop-1 bash -c "ros2 topic echo /robot/your_planner/output --once"
```

Expand Down
10 changes: 5 additions & 5 deletions .agents/skills/update-documentation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ nav:
- Local:
# Existing modules
- Trajectory Library: robot/ros_ws/src/local/planners/trajectory_library/README.md
- DROAN Local Planner: robot/ros_ws/src/local/planners/droan_local_planner/README.md
- Takeoff Landing Planner: robot/ros_ws/src/local/planners/takeoff_landing_planner/README.md
# Add your module HERE
- Your Module Name:
- robot/ros_ws/src/local/planners/your_package/README.md
Expand Down Expand Up @@ -393,8 +393,8 @@ trajectory generation based on local sensor observations.

## Available Modules

### DROAN Local Planner
Disparity-space representation for obstacle avoidance. See [DROAN Local Planner](../../../robot/ros_ws/src/local/planners/droan_local_planner/README.md).
### MIGHTY (default, module)
Map-based Hermite-spline local planner with its acl-mapping voxel world model. See the [mighty module page](../../../docs/modules/mighty.md).

### Your Module Name
Brief one-sentence description of your module.
Expand Down Expand Up @@ -465,7 +465,7 @@ graph TB
GlobalPlanning[Global Planning] -->|Global Plan| LocalPlanning

subgraph "Local Planning Layer"
LocalPlanning --> DROAN[DROAN Planner]
LocalPlanning --> MIGHTY[MIGHTY Planner]
LocalPlanning --> YourModule[Your Module]
LocalPlanning --> TakeoffLanding[Takeoff/Landing]
end
Expand Down Expand Up @@ -739,7 +739,7 @@ feat: Add YourModule local planner

- **AirStack:**
- Documentation template: `../add-ros2-package/assets/package_template/README.md`
- Example README: `robot/ros_ws/src/local/planners/droan_local_planner/README.md`
- Example README: `robot/ros_ws/src/local/planners/takeoff_landing_planner/README.md`
- mkdocs.yml: Repository root

- **Related Skills:**
Expand Down
7 changes: 5 additions & 2 deletions .agents/skills/use-airstack-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,11 @@ airstack stack diff <a> <b> [--json] # compare generated wiring graphs (nodes/ed
airstack up --stack <name>[:<entry>] # run one
```

Reference stacks today: `full_default` (the no-stack default), `full_droan_cpu`,
`full_macvo`, `lite_default`, `lite_offload_global` (split: `:onboard`/`:offboard`).
Reference stacks today: `full_default` (the no-stack default; MIGHTY planner via
the `asm_mighty` module), `full_droan`, `full_droan_cpu`, `full_macvo`
(`asm_droan` module), `lite_default`, `lite_offload_global` (split:
`:onboard`/`:offboard`). `airstack up` reconciles the selected stack's
`modules.repos` pins (add + sync + `lock --build`) before launching.

### Fleets

Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/write-launch-file/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ A stack folder (`stacks/<name>/`) is the unit of topology — see [docs/developm
2. **One `<include>` per module.** Pass the module's declared topic args to wire it:

```xml
<!-- Local planner: disparity from MAC-VO instead of stereo -->
<!-- Local planner (asm_droan module): disparity from MAC-VO instead of stereo — stacks/full_macvo -->
<include file="$(find-pkg-share droan_gl)/launch/droan_gl.launch.xml">
<arg name="droan_gl_disparity_topic"
value="/$(env ROBOT_NAME)/perception/macvo/disparity" />
Expand Down Expand Up @@ -191,7 +191,7 @@ If `ros2 node info` shows a node subscribing to `/odometry` instead of `/<robot_

- **Stacks:** [docs/development/stacks.md](../../../docs/development/stacks.md) — anatomy, wiring.md generation, the AUTONOMY_ROLE removal/migration table
- **Canonical topic names:** [docs/robot/autonomy/integration_checklist.md](../../../docs/robot/autonomy/integration_checklist.md)
- **Reference stack launch files:** `stacks/full_default/launch/stack.launch.xml`, `stacks/full_droan_cpu/`, `stacks/full_macvo/`
- **Reference stack launch files:** `stacks/full_default/launch/stack.launch.xml` (MIGHTY module include), `stacks/full_droan/`, `stacks/full_droan_cpu/`, `stacks/full_macvo/`
- **Lint:** `tests/meta/test_launch_single_locus.py` + `tests/meta/launch_lint_allowlist.txt`
- **ROS 2 docs:** [Launch tutorials](https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Launch/Launch-Main.html) · [Launch XML format](https://design.ros2.org/articles/roslaunch_xml.html)
- **Related skills:** [add-ros2-package](../add-ros2-package) · [integrate-module-into-layer](../integrate-module-into-layer) (legacy wrap-form path) · [debug-module](../debug-module) · [test-in-simulation](../test-in-simulation)
6 changes: 3 additions & 3 deletions .agents/skills/write-mkdocs-documentation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ The `mkdocs-same-dir` plugin is enabled, allowing references to files outside `d
nav:
- Robot:
- Local Planners:
- DROAN: robot/ros_ws/src/local/planners/droan_local_planner/README.md
- Takeoff Landing Planner: robot/ros_ws/src/local/planners/takeoff_landing_planner/README.md
- My Planner: robot/ros_ws/src/local/planners/my_planner/README.md
```

Expand All @@ -223,7 +223,7 @@ nav:
- Module-specific interfaces, parameters, configuration
- Usage examples for that specific module

**Example:** `robot/ros_ws/src/local/planners/droan_local_planner/README.md`
**Example:** `robot/ros_ws/src/local/planners/takeoff_landing_planner/README.md`

### System-Level Documentation

Expand Down Expand Up @@ -1071,7 +1071,7 @@ docs: Add comprehensive guide for new feature
- [GitHub Flavored Markdown](https://github.github.com/gfm/)

**AirStack Examples:**
- Module README: `robot/ros_ws/src/local/planners/droan_local_planner/README.md`
- Module README: `robot/ros_ws/src/local/planners/takeoff_landing_planner/README.md`
- System docs: `docs/robot/autonomy/system_architecture.md`
- Tutorial: `docs/development/beginner/key_concepts.md`

Expand Down
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ PROJECT_NAME="airstack"
# If you've run ./airstack.sh setup, then this will auto-generate from the git commit hash every time a change is made
# to a Dockerfile or docker-compose.yaml file. Otherwise this can also be set explicitly to make a release version.
# auto-generated from git commit hash
VERSION="0.21.0-dev.10"
VERSION="0.21.0-dev.11"
# Image-tag discriminator ONLY (appears in the image tag suffix, e.g. ..._robot-x86-64_dev).
# No Dockerfile consumes it: "prebuilt" does NOT bake the built ros_ws into the image today —
# a real prebuilt (workspace-baked) stage is future work. Keep "dev" (mounted code, built live).
Expand Down
6 changes: 0 additions & 6 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,8 @@
"/home/andrew/Development/AirStack/robot/ros_ws/src/interface/robot_interface/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/controls/mav_comm/mav_msgs/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/controls/mav_comm/mav_planning_msgs/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/planners/droan_gl/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/planners/droan_local_planner/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/planners/takeoff_landing_planner/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/planners/trajectory_library/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/world_models/cost_map_interface/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/world_models/disparity_expansion/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/world_models/disparity_graph/include/**",
"/home/andrew/Development/AirStack/robot/ros_ws/src/local/world_models/disparity_graph_cost_map/include/**",
"/home/andrew/Development/AirStack/simulation/simple-sim/ros_ws/src/sim/include/**",
"/usr/include/**"
],
Expand Down
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Sensors → Perception → World Models → Planners → Controllers → Interfa
```

Each layer has:
- **Module packages**: Individual algorithm implementations (e.g., `droan_local_planner`)
- **Module packages**: Individual algorithm implementations (e.g., `takeoff_landing_planner`, or a module repo's `mighty_bridge`)
- **Stack entry launch files**: The launch topology and topic remapping live in the selected stack (e.g., `stacks/full_default/launch/stack.launch.xml`), not in per-layer bringup packages (the old `local_bringup`-style packages were removed)

**Key Insight:** Understanding "what connects to what" is critical. See [Integration Checklist](docs/robot/autonomy/integration_checklist.md) and [System Architecture](docs/robot/autonomy/system_architecture.md).
Expand Down Expand Up @@ -150,8 +150,8 @@ Study these well-structured modules as examples for different types:

| Module Type | Reference Package | Location |
|------------|------------------|----------|
| **Local Planner** | DROAN Local Planner | `robot/ros_ws/src/local/planners/droan_local_planner` |
| **Local World Model** | Disparity Expansion | `robot/ros_ws/src/local/world_models/disparity_expansion` |
| **Local Planner** | MIGHTY bridge (module, default) / DROAN Local Planner (module) | [asm_mighty](https://github.com/castacks/asm_mighty) `mighty_bridge/` · [asm_droan](https://github.com/castacks/asm_droan) `droan_local_planner/` |
| **Local World Model** | Disparity Expansion (module) | [asm_droan](https://github.com/castacks/asm_droan) `disparity_expansion/` |
| **Controller** | Trajectory Controller | `robot/ros_ws/src/local/controls/trajectory_controller` |
| **Global Planner** | Random Walk | `robot/ros_ws/src/global/planners/random_walk` |
| **Global World Model** | VDB Mapping | `robot/ros_ws/src/global/world_models/vdb_mapping_ros2` |
Expand Down
120 changes: 120 additions & 0 deletions airstack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ function print_command_help {
echo " --stack NAME Launch a stack folder (stacks/NAME/launch/stack.launch.xml)."
echo " Stacks are the only launch dispatch; no --stack (and no stack"
echo " env) launches the trunk reference stack full_default."
echo " The stack's modules.repos pins are reconciled before launch:"
echo " missing modules are added + synced, and their dependency"
echo " layers composed (airstack module lock --build) when absent."
echo " NAME:ENTRY selects an alternate entry file"
echo " (launch/ENTRY.launch.xml). See docs/development/stacks.md."
echo " --fleet NAME Launch a fleet (config/fleets/NAME.yaml): exports FLEET_CONFIG_FILE,"
Expand Down Expand Up @@ -1693,6 +1696,116 @@ function preflight_up {
return $errors
}

# Reconcile the selected stack's module pins with the checkout (RFC #379 §3:
# a stack's pinned modules.repos IS its release set). For every git repo the
# stack's stacks/<name>/modules.repos pins:
# - absent from the checkout-root modules.repos → added at the stack's pin
# (then one `airstack module sync`);
# - present at a DIFFERENT pin → left alone and named (an explicit local
# deviation wins; every deviation is reported, never silently overridden);
# - present at the same pin but not checked out under modules/ → synced.
# Then, when the layer plan has docker-relevant steps and the generated
# compose carries no image override, the dependency layers are composed
# (`airstack module lock --build`) unless AIRSTACK_NO_IMAGE_BUILD=1 forbids
# implicit builds. --dry-run / --config-only only REPORT what would happen —
# they never touch modules.repos, clone, or build. Opt out entirely with
# AIRSTACK_NO_STACK_MODULE_SYNC=1.
function reconcile_stack_modules {
[[ "${AIRSTACK_NO_STACK_MODULE_SYNC:-}" == "1" ]] && return 0
local stack_dir_container stack_name stack_repos
stack_dir_container=$(resolve_launch_var AIRSTACK_STACK_DIR "$@")
stack_name="${stack_dir_container##*/}"
[[ -z "$stack_name" ]] && return 0
stack_repos="$PROJECT_ROOT/stacks/$stack_name/modules.repos"
[[ -f "$stack_repos" ]] || return 0
command -v python3 >/dev/null 2>&1 || return 0

# One line per pinned git module: <name>\t<url>\t<version>\t<status>
# status ∈ add | resync | deviates:<local-version> | ok
local plan
plan=$(STACK_REPOS="$stack_repos" ROOT_REPOS="$PROJECT_ROOT/modules.repos" \
MODULES_DIR="$PROJECT_ROOT/modules" python3 - <<'PY'
import os, yaml
def load(path):
if not os.path.isfile(path):
return {}
with open(path, encoding="utf-8") as f:
return yaml.safe_load(f) or {}
stack = (load(os.environ["STACK_REPOS"])).get("repositories") or {}
root_doc = load(os.environ["ROOT_REPOS"])
root = root_doc.get("repositories") or {}
# a local-path module of the same name is an explicit deviation too
local = {e.get("name"): e.get("path", "") for e in (root_doc.get("x-local-modules") or []) if isinstance(e, dict)}
mods = os.environ["MODULES_DIR"]
for name, entry in sorted(stack.items()):
if not isinstance(entry, dict) or entry.get("type", "git") != "git":
continue
url, ver = entry.get("url", ""), str(entry.get("version", ""))
checkout = os.path.join(mods, name)
if name in local:
status = "deviates:local path " + local[name]
elif name not in root:
status = "add"
elif str(root[name].get("version", "")) != ver:
status = "deviates:" + str(root[name].get("version", ""))
elif not (os.path.isdir(os.path.join(checkout, ".git")) or os.path.islink(checkout)):
status = "resync"
else:
status = "ok"
print(f"{name}\t{url}\t{ver}\t{status}")
PY
) || return 0
[[ -z "$plan" ]] && return 0

local report_only=0
[[ "$AIRSTACK_DRY_RUN" == "1" || "$AIRSTACK_CONFIG_ONLY" == "1" ]] && report_only=1

local need_sync=0 name url ver status
while IFS=$'\t' read -r name url ver status; do
[[ -z "$name" ]] && continue
case "$status" in
add)
if (( report_only )); then
log_info "stack $stack_name pins module $name @ $ver — would add + sync (skipped: dry-run)"
else
log_info "stack $stack_name pins module $name @ $ver — adding to modules.repos"
MODULE_ENTRY_NAME="$name" MODULE_ENTRY_KIND="git" \
MODULE_ENTRY_URL="$url" MODULE_ENTRY_VERSION="$ver" _module_repos_upsert || return 1
fi
need_sync=1 ;;
resync)
(( report_only )) || log_info "stack $stack_name pins module $name @ $ver — checkout missing, syncing"
need_sync=1 ;;
deviates:*)
log_warn "stack $stack_name pins module $name @ $ver but this checkout has it @ ${status#deviates:} — keeping the checkout's pin (named deviation)" ;;
esac
done <<< "$plan"

if (( need_sync )) && (( ! report_only )); then
cmd_module_sync || return 1
fi

# Dependency layers: plan has steps but nothing composed → build (implicit
# image build, same policy as compose's own missing-image build).
local plan_json="$PROJECT_ROOT/.airstack/generated/layer_plan.json"
local gen_compose="$PROJECT_ROOT/.airstack/generated/docker-compose.modules.yaml"
if [[ -f "$plan_json" ]] && ! grep -q "image:" "$gen_compose" 2>/dev/null && \
LAYER_PLAN_JSON="$plan_json" python3 -c '
import json, os, sys
plan = json.load(open(os.environ["LAYER_PLAN_JSON"]))
sys.exit(0 if any(h.get("steps") for h in plan.values()) else 1)' 2>/dev/null; then
if (( report_only )); then
log_info "module dependency layers are planned but not composed — would run: airstack module lock --build (skipped: dry-run)"
elif [[ "${AIRSTACK_NO_IMAGE_BUILD:-}" == "1" ]]; then
log_warn "module dependency layers are planned but not composed and AIRSTACK_NO_IMAGE_BUILD=1 — containers will run the BASE image. Run: airstack module lock --build"
else
log_info "Composing module dependency layers (airstack module lock --build)..."
cmd_module_lock --build || return 1
fi
fi
return 0
}

function cmd_up {
# Airstack launch-intent flags (consumed before compose sees the args)
local rest_args=()
Expand All @@ -1701,6 +1814,13 @@ function cmd_up {
if [[ "$AIRSTACK_CONFIG_ONLY" != "1" ]]; then
check_docker
fi
# Stack module pins → checkout (add/sync/compose as needed; reports only
# under --dry-run / --config-only). Runs before the module overlay check
# below so a freshly synced module's compose override is picked up.
if ! reconcile_stack_modules "${rest_args[@]}"; then
log_error "Could not reconcile the selected stack's module pins — not starting services. (AIRSTACK_NO_STACK_MODULE_SYNC=1 to skip.)"
exit 1
fi

local global_args=()
local subcmd_args=()
Expand Down
Loading
Loading