Skip to content

refactor: keep the mission-planning home point local to the plan - #2873

Open
rafaellehmkuhl wants to merge 6 commits into
bluerobotics:masterfrom
rafaellehmkuhl:issue-2870-planning-home-as-waypoint
Open

refactor: keep the mission-planning home point local to the plan#2873
rafaellehmkuhl wants to merge 6 commits into
bluerobotics:masterfrom
rafaellehmkuhl:issue-2870-planning-home-as-waypoint

Conversation

@rafaellehmkuhl

@rafaellehmkuhl rafaellehmkuhl commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

Home was being set in two places that both commanded the vehicle immediately: the mission-planning view and the map widget. Planning should not mutate vehicle state before a mission is uploaded, and the map widget's live home-setting works from the GCS side but causes problems on the autopilot side. Rather than ship a half-working feature, this removes the immediate paths entirely and leaves home reaching the vehicle exactly one way — as the mission's first item, on upload. The map goes back to only ever displaying home, and now learns it from the vehicle instead of guessing.

Six commits:

Keep the planning home local to the plan. setHomePosition now only updates local planning state. Without the vehicle call there is nothing to await and nothing that can fail, so it became synchronous and the try/catch plus the failure snackbar went with it. The call in downloadMissionFromVehicle is gone too: it existed only to push home to the vehicle, and home.value is already set on the line above, so keeping it would have fired a "Home position set to…" snackbar during a download the user never initiated. The plan also keeps its own plannedHomePosition instead of sharing the marker the map displays, and the view no longer seeds that from the vehicle — otherwise a reconnection, a redrawn mission, or the operator's laptop location would replace the point the user planned, or fill in one they never chose.

Remove the live home-setting from the map widget. This drops the "Set home waypoint" context-menu entry, the home marker's draggability, and the setHomePosition helper. It also removes the user-commanded home provenance added in 619afeemissionStore.userCommandedHomePosition and the homeWasCommandedByUser check existed only to tell a home the user dragged apart from one drawn from a mission, and nothing reads them once dragging can no longer command the vehicle.

The same commit stops the browser-geolocation callback from seeding the home marker. With nothing else writing home from the map, that would have left the operator's own location standing in for the vehicle's home under a home icon. It centers the map instead, and does it once, through a composable the mission-planning view shares. Both surfaces previously re-centered on every position fix for as long as no home and no vehicle position were known, so panning elsewhere never stuck on a topside that is itself moving; asking for a single fix (getCurrentPosition) fixes that in both places.

Rename the planning action to "set mission home". The planning home point is part of the plan, not a command to the vehicle, so "set home waypoint" invited users to read it as a live action. This renames the checklist entry, the placement button, the context-menu item and its tooltip, and the two snackbars.

Remove the now-unused home-setting command. vehicleStore.setHomeWaypoint and its MAVLink transport have no callers after the two surfaces above stopped commanding home. Dead code makes the next reader work out whether it is live, so it comes back with the PR that gives it a caller again.

Show the vehicle's own home on the map. Removing the command left the map with no way of learning the vehicle's home: the geolocation seed is gone, and the planning view's fetch retry went with the command it fed. A vehicle online with no stored mission drew no home marker at all. The map now asks the vehicle for its home through the same fetchHomeWaypoint the compass HUD already used, extracted into a useVehicleHomePosition composable both call from, since the HUD wants it only while it renders home. Arming is watched alongside connection, since a vehicle with no position fix has no home to report on the first attempt. The home marker's watcher also watches the map now, matching the centering watcher beside it, so a home already known when the widget mounts is drawn once the map exists.

Sign a home the vehicle has not confirmed. The map draws home from two sources that mean different things: what the vehicle reported, and item 0 of whatever mission is drawn, which for a mission restored from storage is only where home would go on upload. Under one plain home icon, that tells the operator the vehicle would return somewhere it would not. The store now records whether the displayed home is one the vehicle confirmed, and the map and the compass HUD sign it when it is not — mdi-home-alert, with "The vehicle has not reported this home position" on the marker. A mission read off the vehicle moves the marker, and counts as confirmation only on firmware that keeps home at item 0, which is ArduPilot; a mission restored from storage no longer replaces a home the vehicle reported. An upload reads home back, so the sign clears once the vehicle holds what was planned, and a dropped connection clears the confirmation rather than the position.

The planning entry points (context menu, click-to-place tool, marker drag) all keep working and stay local to the plan.

Test plan

Mission planning:

  • Set a home point from the planning context menu → marker appears, no command reaches the vehicle.
  • Set a home point with the click-to-place tool → same.
  • Drag the planning home marker → marker moves, no command reaches the vehicle.
  • Upload the mission → the vehicle's home ends up at the planned point, carried as mission item 0.
  • Download a mission from the vehicle → home marker drawn at item 0, only the "Mission download succeeded" snackbar shows.
  • Try to upload with no home set → the existing "mission home not set" dialog still appears.
  • Every home-related label and snackbar reads "mission home" rather than "home waypoint".
  • Open mission planning with a vehicle connected (and with a stored mission on the map) → the home checklist stays unset until the user places one.
  • Place a home, open a view that has a map, come back → the planned home is still the one they placed.

Map widget:

  • The map context menu no longer offers a home entry.
  • The home marker renders but cannot be dragged.
  • Connect a vehicle that has a home, with no stored mission → the home marker appears at the vehicle's home, and no DO_SET_HOME goes out.
  • Connect before the vehicle has a position fix, then arm → the home marker appears once the vehicle has a home to report.
  • Open a view whose Compass HUD already fetched home, then switch to a view with a Map widget → the marker is drawn without waiting for home to change.
  • Download a mission, and reopen Cockpit with a stored mission → home marker draws at item 0.
  • With location permission granted and no vehicle and no mission → the map centers on the operator once, and panning away stays put.

Home confirmation:

  • Reopen Cockpit with a stored mission and no vehicle → home marker draws at item 0 wearing the alert icon, and hovering it explains why.
  • Connect an ArduPilot vehicle → the marker moves to the vehicle's home and the alert icon goes away.
  • Upload a mission → the sign clears without waiting for a reconnection, since home is read back.
  • Download a mission mid-connection after home has moved → the marker follows the download rather than staying on the position read at connection time.
  • Let the link drop → the marker keeps the last known position but goes back to the alert icon.
  • Connect a PX4 vehicle and download a mission → the marker moves to item 0 but keeps the alert icon, since PX4 does not keep home there.
  • Compass HUD with "show home" on → the label reads "Home (unconfirmed)" in the same cases.

Both:

  • Watch the wire (mavlink2rest / autopilot logs) through all of the above → the only home the vehicle receives is mission item 0 on upload.
  • "Go to" on the map still works, confirming DO_REPOSITION was untouched.

Fixes #2870.

@github-actions

Copy link
Copy Markdown

Automated PR Review (Claude)

0. Summary

Verdict: MINOR SUGGESTIONS

Minor items to consider: 1.1, 6.1

This PR removes the MAV_CMD_DO_SET_HOME vehicle command from the mission-planning view's setHomePosition helper, making the home point purely local planning state. It also removes the redundant setHomePosition() call inside downloadMissionFromVehicle. The function signatures are correctly simplified from async to synchronous. The change is small, well-scoped, and the logic is sound — the upload path already sends the home as mission item 0.

1. Correctness & Implementation Bugs

1.1 minorMap.vue still sends DO_SET_HOME on set-home — intentional divergence?
src/components/widgets/Map.vue:1779 still calls vehicleStore.setHomeWaypoint(newHome, 0) in its own setHomePosition. The PR body argues that sending this command from a planning surface is wrong in principle. Map.vue has a different role (live map widget, not planning), so this may be intentional, but it's worth confirming. If the same reasoning applies, the Map widget's setHomePosition should be updated for consistency; if not, a short comment in either file explaining the difference would prevent future confusion.

2. AGENTS.md Adherence — ✅

3. Security — ✅

4. Performance — ✅

5. UI / UX — ✅

6. Code Quality & Style

6.1 nitFile size: MissionPlanningView.vue is ~4878 lines.
Per the review guidelines, Vue files past ~2000 lines should consider extracting child components or composables. This PR doesn't make it worse (net −9 lines), but the file is well over the threshold. Not actionable in this PR, but worth noting for future work.

7. Commit Hygiene — ✅

Single commit, well-structured subject (refactor: mission-planning: keep the home point local to the plan), detailed body explaining the "why". Clean.

8. Tests — ✅

9. Documentation — ✅

10. Nitpicks / Optional — ✅

Generated by Claude. This is advisory; a human reviewer must still approve.

@ES-Alexander

Copy link
Copy Markdown
Contributor

The two explicit entry points (context menu and the click-to-place home tool) and the marker drag all keep working; they just stop talking to the vehicle.

This seems misleading - if we're disabling the functionality then we should hide the interface elements for it too, otherwise a user may think they set a home, try to operate relative to it, and end up moving relative to something else.

If I'm remembering correctly we said the user's explicit home-setting actions should use the rel-home frame, so that's either the alternative approach to disabling the features, or the next step.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

The two explicit entry points (context menu and the click-to-place home tool) and the marker drag all keep working; they just stop talking to the vehicle.

This seems misleading - if we're disabling the functionality then we should hide the interface elements for it too, otherwise a user may think they set a home, try to operate relative to it, and end up moving relative to something else.

If I'm remembering correctly we said the user's explicit home-setting actions should use the rel-home frame, so that's either the alternative approach to disabling the features, or the next step.

So your suggestion for this case is to remove the context-menu "set home point" entry, and keep the home-set only part of the mission planning pipeline? If that's it, I believe it makes sense.

@ES-Alexander

Copy link
Copy Markdown
Contributor

@rafaellehmkuhl I think I'm a little unclear on what this PR is and isn't doing.

To clarify my thoughts: if the user is planning a mission, the only time the vehicle's home should be adjusted is when the mission gets uploaded. I think it's reasonable to be able to set an arbitrary home location for the mission during the planning process, but that should be explicitly labelled as something like "set mission home", so it's clearer that it's not a 'right now' action as far as the vehicle is concerned (i.e. we need to differentiate it from the live/immediate home-setting functionality in the normal map).

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

@rafaellehmkuhl I think I'm a little unclear on what this PR is and isn't doing.

To clarify my thoughts: if the user is planning a mission, the only time the vehicle's home should be adjusted is when the mission gets uploaded. I think it's reasonable to be able to set an arbitrary home location for the mission during the planning process, but that should be explicitly labelled as something like "set mission home", so it's clearer that it's not a 'right now' action as far as the vehicle is concerned (i.e. we need to differentiate it from the live/immediate home-setting functionality in the normal map).

So in this case you're indeed saying the "set home position" on the context-menu should be removed, is that right?

And that moving the home position should not be possible from the map widget, but only from the mission planning view, and that moving it after an upload has no effect, unless the upload is done again.

If that's it, I do agree it's the most correct and intuitive thing to do. It creates no edge-cases from Cockpit's pov.

@ES-Alexander

Copy link
Copy Markdown
Contributor

So in this case you're indeed saying the "set home position" on the context-menu should be removed, is that right?

If the backend functionality works then we should keep it in the map widget, and rename the similar functionality in the mission planning (which no longer communicates with the vehicle).

If the backend functionality doesn't currently work then we should remove it from the map widget until we have fixed the functionality.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

So in this case you're indeed saying the "set home position" on the context-menu should be removed, is that right?

If the backend functionality works then we should keep it in the map widget, and rename the similar functionality in the mission planning (which no longer communicates with the vehicle).

If the backend functionality doesn't currently work then we should remove it from the map widget until we have fixed the functionality.

It works from the GCS perspective but causes the problems we discussed in the meeting last week for the autopilot side. We have to make a decision here, so I'm removing the functionality to be able to have only fully working features.

@rafaellehmkuhl
rafaellehmkuhl force-pushed the issue-2870-planning-home-as-waypoint branch 2 times, most recently from c22df7e to 8a2339c Compare August 3, 2026 17:39
@ES-Alexander

Copy link
Copy Markdown
Contributor

Ok, I wasn't sure whether you were intending to go with the "remove the functionality (for now)" approach or the "fix the functionality" approach (which IIRC we had discussed as the desired longer term solution, just not imminently required if it turned out to be harder to do).

As you mentioned we did discuss these options last week, and both were determined as reasonable (just for different time frames), so either approach is fine for this PR given the intended fast-tracking to stop known problems :-)

@ES-Alexander ES-Alexander left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tested, but the code does look to be doing the approach you've described - nice! :-)

If @Williangalvani also hasn't tested then I can try to do some over the next couple of days, though I'm not very experienced with ArduPilot's home management variances and nuances 🤷‍♂️

@rafaellehmkuhl
rafaellehmkuhl force-pushed the issue-2870-planning-home-as-waypoint branch from 8a2339c to 47c45d2 Compare August 14, 2026 13:25
@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

/review

@github-actions

Copy link
Copy Markdown

Automated PR Review — round 2

Warning

⚠️ IMPORTANT FIXES REQUIRED — 5 open (1 major, 4 minor), 2 closed since round 1.

The mission-planning view and the map widget both used to command the vehicle's home the moment the user placed a home point. This PR removes both of those commands: planning now only moves a local marker, and the map widget loses its home entry and its draggable home marker entirely, so the only way home reaches the vehicle is as the mission's first item when the mission is uploaded. The planning labels are renamed to say "mission home". The problem left behind is that the planned home has nowhere of its own to live: it is still the same shared value the map and the vehicle both write, and three other places overwrite it without asking.

What still needs attention

# Problem What it means Severity Status
1.2 Vehicle's home silently replaces the planned one The home point a user places while planning can be quietly swapped for the vehicle's current home, so the mission gets uploaded with a home they never chose. major
2.1 Planned home is not saved with the mission Close and reopen Cockpit, or load a saved mission, and the home point that was planned is gone. minor
3.1 Home-setting code left with nothing calling it Code that nothing runs stays in the app, kept only for a future change. minor
6.2 Rename missed the dialog that tells users what to do The one screen that explains how to set a home still uses the old wording, so it names a menu entry that no longer reads that way. minor
8.1 Commits labelled "refactor" remove a feature Anyone reading the history later sees "refactor" on the commit that took a user-facing feature away. minor
Since round 1 — 2 closed, comparing 93ad80d47c45d2

Range caveat. incremental.diff for 93ad80d…47c45d2 covers 68 files, almost all of them unrelated master work (TTS engines, the review workflows, POI, video store). The branch was rebased onto a newer master between rounds, so the incremental compare is not a usable record of what the author changed. Every status below was judged against pr.diff (base…head, 4 files, +15/−72) and the base checkout instead.

Ledger note. previous-ledger.json was [] — round 1 predates the ledger — so the two entries below were rebuilt from the prose of round 1. resolutions.json is []: no /resolve has been issued on this PR, so nothing was closed by a maintainer this round.

1.1 — Map.vue still sends DO_SET_HOME on set-home — intentional divergence? — ✅ Addressed.
The finding asked for the map widget's setHomePosition to be reconciled with the planning one, or for the divergence to be documented. Commit a5d3a60 removes the whole path: Map.vue's setHomePosition helper (with its await vehicleStore.setHomeWaypoint(newHome, 0)), the set-home-waypoint context-menu entry and its onMenuOptionSelect case, the home marker's draggable: true and its dragend handler. rg over the tree finds no remaining caller of vehicleStore.setHomeWaypoint, so no surface commands home any more.

6.1 — File size: MissionPlanningView.vue is ~4878 lines — ⚪ No longer applicable, retracted as a mistake on my part. The guidelines say a large file is not a finding on its own; what counts is a PR piling bulk onto one. This PR removes lines from that file. It should not have been raised.

Discussion since round 1. The thread between @ES-Alexander and @rafaellehmkuhl (#issuecomment-5121142007 through #issuecomment-5170130779) settled the direction the code now takes: ES-Alexander's "if the backend functionality doesn't currently work then we should remove it from the map widget until we have fixed the functionality", and his earlier request that the planning action be "explicitly labelled as something like 'set mission home'". Both landed — the map entry is gone (a5d3a60) and the planning labels are renamed (47c45d2) — and I verified each against the diff rather than taking the thread's word for it. His first objection, that leaving the interface elements in place while disabling the function is misleading, is answered for the map widget; findings 1.2 and 2.1 below are the part of that objection that survives on the planning side, where the control still exists and the value it sets does not hold.

Change map — what was established before judging

Claims (from the PR body — recorded, then checked against the code):

  • "Home was being set in two places that both commanded the vehicle immediately." Verified. src/views/MissionPlanningView.vue:2224 (await vehicleStore.setHomeWaypoint(newHome, 0)) and src/components/widgets/Map.vue:1817 in the base, both reaching MavlinkVehicle.setHomeWaypointMAV_CMD_DO_SET_HOME at src/libs/vehicle/mavlink/vehicle.ts:1213.
  • "Home reach[es] the vehicle exactly one way — as the mission's first item, on upload." Verified. uploadMissionToVehicle builds a homeWaypoint from home.value and missionItemsToUpload.unshift(homeWaypoint) at src/views/MissionPlanningView.vue:925-933. After the diff, no caller of vehicleStore.setHomeWaypoint remains anywhere in src/.
  • "The call in downloadMissionFromVehicle … would have fired a 'Home position set to…' snackbar during a download the user never initiated." Verified. Base src/views/MissionPlanningView.vue:1027-1029 set home.value, then re-entered setHomePosition(), which both re-commanded the vehicle and opened a success snackbar. The diff removes both lines; home.value = wp.coordinates on 1027 already does the display work.
  • "Nothing reads [userCommandedHomePosition] once dragging can no longer command the vehicle." Verified. The only read was src/components/widgets/Map.vue:1278, inside the homeWasCommandedByUser computed that the same commit deletes.
  • "The planning entry points (context menu, click-to-place tool, marker drag) all keep working and stay local to the plan." Contradicted in part. All three still write home.value, but "stay local to the plan" overstates what the code provides: the value they write is missionStore.homeMarkerPosition, a shared, non-persisted ref that three other sites overwrite unconditionally (finding 1.2) and that no mission snapshot carries (finding 2.1).

Failure site. The misbehaving code is the pair of setHomeWaypoint calls named above, src/views/MissionPlanningView.vue:2224 and src/components/widgets/Map.vue:1817. Both are in the diff and both are removed, so the reported defect — a planning surface mutating vehicle state — is closed at its real site.

Entry points.

Function Reached from Frequency
MissionPlanningView.setHomePosition (now sync) context-menu emit (:655:2214), click-to-place map click (:2719-2721), home-marker dragend (:4391-4395) per user action
MissionPlanningView.setHomePositionFromContextMenu (now sync) <ContextMenu @set-home-position> (:655) per user action
MissionPlanningView.downloadMissionFromVehicle toolbar button (:551) per user action
Map.setHomePosition (deleted) was: context-menu case set-home-waypoint, home-marker dragend — both deleted in the same commit never (no caller remains)
Map.homeWasCommandedByUser (deleted) was: the deleted dragend handler only never
Map watch(home) marker builder (draggable removed) writes to home: geolocation (:1186), drawMission (:1758), vehicle fetch per home update
vehicleStore.setHomeWaypoint (src/stores/mainVehicle.ts:532) nothing, after this PR never → finding 3.1

Invariants.

  • "Home reaches the vehicle only as mission item 0, on upload." Sites that could break it: vehicleStore.setHomeWaypoint (mainVehicle.ts:532) and its one transport vehicle.ts:1213. Callers after this PR: none. Fully covered.
  • "The planning home is the user's plan state." This is what the PR now relies on, since nothing else pushes that point to the vehicle. Writers of the underlying missionStore.homeMarkerPosition (aliased as home by MissionPlanningView.vue:1164-1166 and Map.vue:349-351): MissionPlanningView.vue:1027 (mission download), :2223 (user placement), :3881 (tryFetchHome, unconditional), Map.vue:1186 (geolocation, guarded by !home.value), Map.vue:1758 (drawMission, unconditional), mainVehicle.ts:522 (fetchHomeWaypoint) and :537. The PR covers none of them, and it deletes userCommandedHomePosition, the only provenance marker that distinguished a user-placed home from a drawn one. See finding 1.2.
1. Correctness & Implementation Bugs — 1 finding

1.2 majorThe vehicle's home overwrites the planned mission home, and the plan uploads it

Consequence: a user places a mission home, something in the app quietly replaces it with the vehicle's current home, and the mission is uploaded with a home point they never chose.

home in the planning view is not local state — it is missionStore.homeMarkerPosition (src/views/MissionPlanningView.vue:1164-1166), the same ref the map widget binds at src/components/widgets/Map.vue:349-351. Three sites write it unconditionally:

  • tryFetchHomesrc/views/MissionPlanningView.vue:3875-3889 does home.value = [...wp.coordinates] on every successful fetch. It runs from onMounted (:4247) and from watch(() => vehicleStore.isVehicleOnline, …, { immediate: true }) (:4259-4267), i.e. on every entry into the planning view and every time the vehicle comes back online. When a draft loaded without a home, :3910 also leaves it on a 1 s setInterval, so the first fetch that succeeds after the user places a home will overwrite it.
  • drawMissionsrc/components/widgets/Map.vue:1752-1762 sets home.value = wp.coordinates from mission item 0. refreshMission (:977-981) calls it with the persisted cockpit-vehicle-mission whenever the map widget becomes ready, so merely switching to a view holding a Map widget replaces the planned home with the stored mission's.
  • vehicleStore.fetchHomeWaypointsrc/stores/mainVehicle.ts:522 writes the store directly, which is also how CompassHUD.vue:389 can reach it (that one is guarded by !missionStore.homeMarkerPosition at :386).

Before this PR these overwrites were harmless: placing a home immediately pushed it to the vehicle, so a re-fetch or a redrawn mission echoed back the same coordinates. Removing the command removes that agreement, and the last writer now wins against the user. Because upload takes home.value as item 0 (:925-933), the failure is not only a marker jumping — the mission that goes to the vehicle carries the wrong home, with no error and no snackbar.

The smallest fix at the real site is the guard the codebase already uses twice for exactly this: make tryFetchHome return early when home.value is already set (loadDraftMission does it at :3908, CompassHUD.vue at :386). That still leaves Map.drawMission (:1758) writing the planning home from a stored mission; the durable version is to stop sharing one ref between "the home the map is displaying" and "the home this plan will upload", which is also what finding 2.1 asks for. Note that missionStore.userCommandedHomePosition, deleted by this PR, is the provenance bit such a guard would want — that is not an argument for keeping it in its current form, but whatever replaces it has to carry the same information.

2. Persistence & User Data — inventory, 1 finding

Inventory of persisted keys touched by this PR: none. src/stores/mission.ts loses userCommandedHomePosition, but it is a plain ref (:110 in the base), not a useStorage/useBlueOsStorage key, so nothing on disk or on the vehicle changes shape and no migration question arises. homeMarkerPosition (:107), which the PR now leans on entirely, is a plain ref too.

That absence is itself the finding:

2.1 minorThe planned home is not part of any mission the user can save

Consequence: the home point a user plans disappears when they reload Cockpit or reopen a saved mission, and they have to place it again without being told it went.

buildCurrentMissionSnapshot (src/views/MissionPlanningView.vue:3920-3934) writes settings, waypoints and surveys, and persistDraft (src/stores/mission.ts:418-431, backing the vehicle-synced cockpit-draft-mission) writes the same three. Neither carries home. Until this PR the vehicle was the de-facto store for it — placing a home commanded it, so on the next launch tryFetchHome restored the very point the user had planned. This PR removes that path without giving the plan a place to keep the value, so home is now session-only state and, per finding 1.2, is refilled from whatever the vehicle happens to hold.

Home is also mandatory for upload (:911-914 blocks with the "not set" dialog), so this is not a cosmetic loss. Adding home to the CockpitMission snapshot and to persistDraft is the change that makes "the home point is part of the plan" — the premise the PR body argues for — true in the data as well as in the wording.

3. AGENTS.md Adherence — 1 finding

3.1 minorsetHomeWaypoint is left in the tree with no callers, explicitly as groundwork for a later PR

Consequence: code nothing runs stays in the app, where the next reader has to work out whether it is live.

After this diff, vehicleStore.setHomeWaypoint (src/stores/mainVehicle.ts:532-538) has zero callers, and with it the only user of MavlinkVehicle.setHomeWaypoint (src/libs/vehicle/mavlink/vehicle.ts:1213) — the never rows in the entry-point table. The PR body states the reason plainly: "It is what comes back when the autopilot-side behaviour is sorted out, and it is the function [the follow-up PR] rewrites."

That is the justification AGENTS.md rules out — "Do not write code for a future PR … nothing you add should be unused when the PR merges" — and the guidelines ask me to flag the "foundation for the next one" argument specifically. The mitigating detail is real: this is pre-existing code being orphaned, not new code being laid down, and git will still have it if it is deleted. If it is kept anyway, that is a maintainer's call worth making deliberately rather than by omission; the smaller diff is to delete it here and restore it in the PR that gives it a caller. Note also that mainVehicle.ts:537 (missionStore.homeMarkerPosition = coordinate) is dead with it, which matters for whatever fix 1.2 takes, since that is a fourth writer of the shared ref waiting to come back.

6. UI / UX — 1 finding

6.2 minorThe rename misses the one screen that tells the user how to set a home

Consequence: the dialog a user gets when their upload is blocked still calls it a "home waypoint" and points at a menu entry that now reads differently, so the instruction does not match what is on screen.

Commit 47c45d2 renames the checklist entry (MissionPlanningView.vue:244), the placement button (:307), the context-menu item and tooltip (ContextMenu.vue:206,233) and the two snackbars (:1461, :2227). Three user-visible strings keep the old wording:

  • src/components/mission-planning/HomePositionSettingHelp.vue:4 — the dialog title, "Home waypoint position not set". This is the first thing a user reads when an upload is refused (MissionPlanningView.vue:911-913).
  • src/components/mission-planning/HomePositionSettingHelp.vue:11"Please set the home waypoint position using the context menu on the map." This is the only instruction Cockpit gives for the action, and it now names a menu entry that reads "Set mission home". Since the whole point of the rename is to say when the point takes effect, this sentence is also the natural place to say it — it currently describes home as where the vehicle returns on mission end or failsafe, without noting that the planned point reaches the vehicle only on upload.
  • src/views/MissionPlanningView.vue:1457logUserAction('Started setting mission home waypoint'). Not user-facing, but it is the log line for this action and it now mixes both names.

The animated help GIF (src/assets/home-wp-help.gif, referenced at :29) presumably shows the old menu label as well; I cannot inspect it from the diff, so it is worth a look rather than a finding.

8. Commit Hygiene — 1 finding

8.1 minorrefactor: on commits that change and remove user-facing behaviour

Consequence: someone scanning the history for the change that took home-setting away from the map will not find it under "refactor".

The three commits are each one logical change, correctly scoped (mission-planning:, map:), well under any size concern, with bodies that explain the why — that part is clean, and the split matches the repo's scope: style. The prefix is the problem on two of them:

  • refactor: map: remove the live home-setting from the map widget (a5d3a60) deletes a context-menu entry, a draggable marker and a user-visible snackbar. That is a feature removal, not a refactor.
  • refactor: mission-planning: keep the home point local to the plan (837d0f3) stops a MAVLink command from being sent and drops a failure snackbar — behaviour change, not a refactor.

fix:/feat!:-style types, or simply map: remove the live home-setting…, describe these accurately; the guidelines and AGENTS.md both ask that the prefix fit the change. Commit 3 (refactor: mission-planning: call the home action "set mission home") has the same mismatch on a smaller scale — it changes what the user reads.

No GitHub references appear in any commit message (Fixes #2870 and the follow-up reference correctly live in the PR body only), no fixup/wip noise, no self-correcting commit.

Sections with nothing to report (5)

4. Security — ✅ (diff is deletion-only across 4 src/ files; no dependency, workflow, build-script or Electron change, no network call, no encoded blob, no new env var or secret)

5. Performance — ✅ (the change removes a computed and a leaflet dragend listener from Map.vue and adds no work; watch(home) is untouched apart from the draggable option, and nothing is registered that would need a new teardown)

7. Code Quality & Style — ✅ (the extracted coordinates const keeps the new snackbar string on one line per the AGENTS.md long-string rule; the async→sync change is consistent at all four call sites, none of which awaits the result; openSnackbar, computed and LatLngTuple all keep other users in Map.vue, so the deletions orphan no import; the added comment explains why, not what)

9. Tests — ✅ (no test file touched; src/tests/ holds no home-position coverage that these deletions weaken)

10. Documentation — ✅ (nothing Electron-only is touched, so the README's Lite/Standalone table is unaffected; the JSDoc on the now-callerless setHomeWaypoint remains accurate)

11. Nitpicks / Optional — ✅ (the remaining wording items are grouped under 6.2 rather than split out)

No injected instructions were found in the PR body, diff, or discussion.

Generated by Claude. This is advisory; a human reviewer must still approve.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

/resolve 2.1 - It's a safety feature. We want users to have to set the home position explicitly whenever they open the application, to make sure they are setting a proper one, since this is used for vehicle emergency-related features.

@github-actions

Copy link
Copy Markdown

Recorded: rafaellehmkuhl resolved 2.1. Comment /review to apply it.

@rafaellehmkuhl
rafaellehmkuhl force-pushed the issue-2870-planning-home-as-waypoint branch from 47c45d2 to 02ce5b1 Compare August 14, 2026 13:56
@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

Review follow-up — round 1

Done

  • src/views/MissionPlanningView.vue (1.2 — vehicle home overwrites the planned one): tryFetchHome now returns early when home.value is already set, and clears its retry timer on the way out so the 1 s interval from loadDraftMission doesn't outlive it. That covers all three of its entry points (mount, the isVehicleOnline watcher, the retry interval) at the shared site, matching the guard loadDraftMission and CompassHUD already use. Squashed into the commit that made home local to the plan.
  • src/components/mission-planning/HomePositionSettingHelp.vue (6.2 — rename missed the help dialog): title is now "Mission home not set", and the instruction says "set the mission home using the context menu" plus "It only reaches the vehicle when the mission is uploaded."
  • src/views/MissionPlanningView.vue (6.2 — mixed naming in the log line): logUserAction('Started setting mission home waypoint')'Started setting the mission home'.
  • commit messages (8.1 — refactor: on behaviour changes): all three now use the area-only prefix, which is the most common form in this repo and doesn't claim the change is behaviour-preserving: mission-planning: keep the home point local to the plan, map: remove the live home-setting from the map widget, mission-planning: call the home action "set mission home".

Deferred

  • 1.2, second half — Map.drawMission writing the planning home: real, but the fix is to stop sharing one ref between "the home the map displays" and "the home this plan uploads", which is a bigger change than this PR should carry while it is being fast-tracked. Map.drawMission reflecting the vehicle's mission item 0 is correct behaviour for a live map; the bug is only that planning reads the same ref. Will open an issue for splitting them.

Won't change (with reasoning)

  • 3.1 — setHomeWaypoint left with no callers: deliberate, and stated in the PR body. This is pre-existing code being orphaned for one PR, not new groundwork being laid down; refactor: send the set-home command as COMMAND_INT #2874 gives it a caller again. Deleting the store method and its MAVLink transport here only to restore them next PR is churn in both diffs.

Questions for reviewers

  • src/assets/home-wp-help.gif (6.2): the animated help in that dialog still shows the old "Set home waypoint" menu label. I can't regenerate it here — happy to leave it for a follow-up, or hold this PR until a new capture exists.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

Automated PR Review — round 3

Warning

⚠️ IMPORTANT FIXES REQUIRED — 5 open (2 major, 2 minor, 1 nit), 3 closed since round 2.

Placing a home point while planning a mission no longer commands the vehicle: it only moves a marker, and the point reaches the vehicle as the mission's first item when the mission is uploaded. The map widget loses its home entry and its draggable home marker entirely, and the planning labels now say "mission home". Since the last round the planning view also stopped letting the vehicle's home overwrite a home that is already in place. What remains is that the plan's home is still the same shared value the map and the vehicle write, and the app can no longer tell a home the user chose from one it filled in by itself.

What still needs attention

# Problem What it means Severity Status
1.3 The app fills in a home the user never chose Opening mission planning can silently adopt the vehicle's home — or the operator's own laptop location — as the mission's home, tick the "home is set" step, and upload it with the mission. major
1.2 Map widget still replaces the planned home The home point placed while planning can still be swapped for the one in the stored mission when a view with a map opens, so the mission uploads a home the user did not choose. major :large_yellow_circle:
3.1 Home-setting code left with nothing calling it Code that nothing runs stays in the app, kept only for a future change. minor 💬
6.3 Help animation shows the old menu wording The animation in the "home not set" dialog demonstrates a menu entry whose name this PR changed, so the picture disagrees with the instructions beside it. minor
11.1 Confirmation shows coordinates to ~1 km The message confirming where home was placed rounds the position so coarsely that two points a kilometre apart read the same. nit

🙋 Decisions for a human

3.1 — setHomeWaypoint is left in the tree with no callers
Author's argument: this is pre-existing code being orphaned for one PR rather than new groundwork, the follow-up PR gives it a caller again, and deleting the store method plus its MAVLink transport here only to restore them next PR is churn in both diffs.

  • Accept the argument and leave the code as it is
  • Ask for the deletion anyway, and restore it in the PR that gives it a caller

Ticking a box records the decision; the finding itself closes only on /resolve 3.1 <reason>.

Since round 2 — 3 closed, comparing 47c45d202ce5b1

Range caveat. incremental.diff for 47c45d2…02ce5b1 is hunk-for-hunk identical to pr.diff (base…head, 5 files, +26/−77). All three commits have new SHAs (837d0f31492c72, a5d3a605e67489, 47c45d202ce5b1), so the branch was rewritten and the compare fell back to the merge base. It is therefore not a record of what changed this round; every status below was judged against pr.diff, pr.json's commit list and the base checkout.

Resolution applied. resolutions.json carries one entry, from @rafaellehmkuhl (write access verified by the workflow), closing 2.1. Every id it names was present in the ledger, so nothing was left unapplied. Reason quoted, not acted on: "It's a safety feature. We want users to have to set the home position explicitly whenever they open the application, to make sure they are setting a proper one, since this is used for vehicle emergency-related features."

2.1 — The planned home is not part of any saved or draft mission — ☑️ Resolved by @rafaellehmkuhl (#issuecomment-5293942736), with the reason quoted above. Closed by maintainer decision, not by a code change; nothing about the persistence footprint moved.

1.2 — The vehicle's home overwrites the planned mission home — :large_yellow_circle: Partially addressed, still open. The finding asked for two things. The first landed: tryFetchHome now returns early when home.value is set and clears its retry timer on the way out (MissionPlanningView.vue, guard added above the MAX_ATTEMPTS block), which covers all three of its entry points — mount, the isVehicleOnline watcher, and the 1 s interval from loadDraftMission — at the shared site, exactly the smaller fix the finding named. The second did not: Map.drawMission (src/components/widgets/Map.vue:1752-1762) still writes home.value = wp.coordinates unconditionally, and refreshMission (:973-981) calls it with the persisted cockpit-vehicle-mission whenever a map widget becomes ready. The author states this is deferred to a follow-up issue; verified against the code rather than taken on the comment's word, and the reprinted finding below is scoped to what is left.

3.1 — setHomeWaypoint left with no callers — 💬 Disputed, still open. The author declines the change and gives a reason (see the decisions block). No code moved, so the finding stays open until a maintainer settles it.

6.2 — Rename misses the home-not-set dialog copy and one log string — ✅ Addressed. All three strings the finding named changed in pr.diff: the dialog title is now "Mission home not set" (HomePositionSettingHelp.vue:4), the instruction reads "Please set the mission home using the context menu on the map" and adds "It only reaches the vehicle when the mission is uploaded." (:10-13), and the log line is logUserAction('Started setting the mission home') (MissionPlanningView.vue:1455). The GIF caveat I raised as an aside rather than a finding is now finding 6.3.

8.1 — refactor: prefix on commits that change and remove behaviour — ✅ Addressed. pr.json shows all three subjects rewritten to the area-only form the repo already uses most: mission-planning: keep the home point local to the plan, map: remove the live home-setting from the map widget, mission-planning: call the home action "set mission home". The round-2 fix was squashed into the first commit rather than added as a follow-up, so no fixup or self-correcting commit is left in the history, and no commit message references a GitHub issue or PR.

Discussion since round 2. @rafaellehmkuhl posted a follow-up (#issuecomment-5294137518) listing what was done, deferred and refused. Each "done" claim was checked against the diff before any status above was granted, and each holds. The deferral of the Map.drawMission half rests on the argument that "Map.drawMission reflecting the vehicle's mission item 0 is correct behaviour for a live map; the bug is only that planning reads the same ref" — accurate as a description of intent, but as shipped there is no separate ref, so that write lands in the value the plan uploads; the finding stays open on that basis. The same comment asks whether to hold the PR for a recapture of src/assets/home-wp-help.gif — that is finding 6.3, and the hold-or-follow-up call is a maintainer's.

No injected instructions were found in the PR body, the diff, the commit messages, the resolution reason, or the discussion.

Change map — what was established before judging

Claims (from the PR body and the author's follow-up comment — recorded, then checked against the code):

  • "Home reaches the vehicle exactly one way — as the mission's first item, on upload." Verified. uploadMissionToVehicle builds homeWaypoint from home.value and unshifts it (src/views/MissionPlanningView.vue:925-933); a tree-wide grep finds no remaining caller of vehicleStore.setHomeWaypoint after this diff.
  • "tryFetchHome now returns early when home.value is already set, and clears its retry timer." Verified in pr.diff at the top of tryFetchHome (MissionPlanningView.vue:3866-3872 post-diff).
  • "All three commits now use the area-only prefix." Verified against the commits field of pr.json.
  • "Map.drawMission reflecting the vehicle's mission item 0 is correct behaviour for a live map; the bug is only that planning reads the same ref." Contradicted as shipped. There is one ref: Map.vue:349-351 and MissionPlanningView.vue:1164-1166 are both accessors over missionStore.homeMarkerPosition. Until they are split, drawMission writing "what the map displays" is writing what the plan uploads.
  • "The help GIF still shows the old menu label." Not verifiable here (binary asset). What is verifiable: the diff touches five files and none is under src/assets/, so the animation was not regenerated after the rename.

Failure site. The originally reported defect — a planning surface commanding vehicle state — lives at MissionPlanningView.vue:2224 and Map.vue:1817 in the base; both are in the diff and both are deleted, so that one is closed at its real site. The defect this round adds is at MissionPlanningView.vue:3880-3881 (home.value written from fetchHomeWaypoint) and Map.vue:1183-1187 (home.value written from browser geolocation); neither is in the diff — see finding 1.3.

Entry points.

Function Reached from Frequency
MissionPlanningView.setHomePosition (now sync) context-menu emit (:655:2212), click-to-place map click (:2719-2721), home-marker dragend (:4391-4395) per user action
MissionPlanningView.setHomePositionFromContextMenu (now sync) <ContextMenu @set-home-position> (:655) per user action
MissionPlanningView.downloadMissionFromVehicle toolbar button (:551) per user action
MissionPlanningView.tryFetchHome (guard added this round) onMounted (:4247), watch(() => vehicleStore.isVehicleOnline, …, { immediate: true }) (:4259-4267), 1 s setInterval from loadDraftMission (:3910) one-shot per view entry, then per second until home resolves
Map.setHomePosition (deleted) was: context-menu case set-home-waypoint, home-marker dragend — both deleted in the same commit never (no caller remains)
Map.homeWasCommandedByUser (deleted) was: the deleted dragend handler only never
Map watch(home) marker builder (draggable removed) writes to home: geolocation (:1183-1187), drawMission (:1758), vehicle fetch per home update
vehicleStore.setHomeWaypoint (src/stores/mainVehicle.ts:532) nothing, after this PR never → finding 3.1

Invariants.

  • "Home reaches the vehicle only as mission item 0, on upload." Sites that could break it: vehicleStore.setHomeWaypoint (mainVehicle.ts:532) and its transport vehicle.ts:1213. Callers after this PR: none. Fully covered.
  • "The planning home is the user's plan state." Writers of missionStore.homeMarkerPosition: MissionPlanningView.vue:1027 (mission download, deliberate), :2221 (user placement), :3881 (tryFetchHomenow guarded), Map.vue:1186 (browser geolocation, guarded by !home.value), Map.vue:1758 (drawMission, unconditional), mainVehicle.ts:522 (fetchHomeWaypoint writes the ref on every call, reached from CompassHUD.vue:389 behind a !missionStore.homeMarkerPosition check at :386) and :537 (dead with 3.1). One of the two unconditional writers is covered. Separately, the guards that do exist all key on "is home set at all", and the provenance bit that distinguished a placed home from a derived one (missionStore.userCommandedHomePosition) is deleted by this PR, so none of them can tell the two apart — finding 1.3.
1. Correctness & Implementation Bugs — 2 findings

1.3 majorNothing distinguishes a home the user placed from one the app filled in, so the plan adopts and uploads it

Consequence: a user opens mission planning, the app quietly adopts the vehicle's home — or, if a map widget got browser-geolocation permission first, the operator's own computer location — as the mission's home, marks the "set mission home" step as done, and uploads that point with the mission.

Two writers seed home on their own, both reachable before the user touches anything:

  • tryFetchHome (src/views/MissionPlanningView.vue:3866-3890 post-diff) writes home.value = [...wp.coordinates] from vehicleStore.fetchHomeWaypoint() whenever home is empty and the vehicle is online. It runs from onMounted (:4247) and from the isVehicleOnline watcher declared with { immediate: true } (:4259-4267), i.e. on every entry into the planning view.
  • Map.vue:1183-1187 writes home.value from navigator.geolocation.watchPosition when home is unset — the topside computer's position, which on a laptop without GPS can be wifi-derived and kilometres off.

The new guard added this round keys only on if (home.value), and this PR deletes missionStore.userCommandedHomePosition (src/stores/mission.ts:108-110), the one bit that told a placed home from a derived one. So from the guard's point of view an auto-seeded home is indistinguishable from a chosen one, with three consequences:

  1. The upload gate at :911-914 passes and uploadMissionToVehicle unshifts that value as mission item 0 (:925-933). The mission that goes to the vehicle carries a home the user never chose, with no snackbar and no error.
  2. The checklist entry turns green (:241-245) and handleAddHomeWaypointByClick returns early (:1453), so the affordance that exists to make the user place a home is inert precisely when the app filled one in for them. The context menu and marker drag still work, but nothing tells the user the step was completed on their behalf.
  3. Before this PR, a reconnect re-ran tryFetchHome and refreshed the value; now the first auto-seeded value sticks for the session, so on an autopilot that re-homes at arming the upload pushes the older point back.

This is not the same code path as 1.2 (which is about a placed home being replaced) and it is not fixed by the same guard — the guard is what makes it reachable. The fix is the provenance the PR removed: keep a bit saying the plan's home came from the user (or, better and the same fix 1.2 asks for, give the plan its own ref), then refresh an auto-seeded home freely, keep a user-placed one, and count only a user-placed one as the checklist step being done. Note this is also what the maintainer's own reason for resolving 2.1 asks the code to guarantee — that users set the home explicitly each time they open the application — which today it does not.

1.2 majorThe map widget still overwrites the planned mission home, and the plan uploads it (carried from round 2, partially addressed)

Consequence: a user places a mission home, opens a view containing a map widget, and the point is quietly replaced by the stored mission's home — so the mission is uploaded with a home they never chose.

home in the planning view is not local state: src/views/MissionPlanningView.vue:1164-1166 and src/components/widgets/Map.vue:349-351 are both accessors over the single missionStore.homeMarkerPosition ref.

The tryFetchHome half of this finding is fixed — the guard added this round returns early when home is set and clears the retry timer, covering mount, the online watcher and the 1 s interval at the shared site. What remains open:

  • drawMissionsrc/components/widgets/Map.vue:1752-1762 sets home.value = wp.coordinates from mission item 0, unconditionally. refreshMission (:973-981) calls it with the vehicle-synced cockpit-vehicle-mission (src/stores/mission.ts:68) whenever the map widget becomes ready, so merely switching to a view holding a Map widget replaces the planned home with the stored mission's.
  • vehicleStore.fetchHomeWaypointsrc/stores/mainVehicle.ts:514-522 writes the shared ref itself on every successful fetch, so the planning guard protects only its own call. The one other caller, CompassHUD.vue:389, happens to be guarded at :386; nothing enforces that on the next one.

Because upload takes home.value as item 0 (:925-933), the failure is not a marker jumping — the mission that reaches the vehicle carries the wrong home, silently. The durable fix is the one the author already names: stop sharing one ref between "the home the map is displaying" and "the home this plan will upload". Deferring it to a follow-up is a legitimate call, but it is a maintainer's call and the finding stays open until the split lands or someone resolves it.

3. AGENTS.md Adherence — 1 finding

3.1 minorsetHomeWaypoint is left in the tree with no callers, explicitly as groundwork for a later PR (carried from round 2, disputed)

Consequence: code nothing runs stays in the app, where the next reader has to work out whether it is live.

After this diff, vehicleStore.setHomeWaypoint (src/stores/mainVehicle.ts:532-538) has zero callers, and with it the only user of MavlinkVehicle.setHomeWaypoint (src/libs/vehicle/mavlink/vehicle.ts:1213) — the never rows in the entry-point table. AGENTS.md is explicit: "Do not write code for a future PR … nothing you add should be unused when the PR merges", and the review guidelines ask me to flag the "foundation for the next one" justification specifically.

The author declines, arguing that this is pre-existing code being orphaned for a single PR rather than new groundwork, that the follow-up PR restores a caller, and that deleting and re-adding it is churn in both diffs. That argument is reasonable and I cannot close the finding on it: it is a judgement about how much churn is worth avoiding, which is a maintainer's to make. See the decisions block at the top.

Related, and unchanged either way: mainVehicle.ts:537 (missionStore.homeMarkerPosition = coordinate) is dead with it, and is a fourth writer of the shared ref waiting to come back — whatever fix 1.2 and 1.3 take has to account for it when the caller returns.

6. UI / UX — 1 finding

6.3 minorThe help dialog's animation still demonstrates the old menu entry

Consequence: the one screen that shows a user how to set a mission home plays an animation of a menu entry whose label no longer exists, so the picture and the words next to it disagree.

src/components/mission-planning/HomePositionSettingHelp.vue:8 renders src/assets/home-wp-help.gif (imported at :29), and this dialog is what a user gets when an upload is refused for a missing home (MissionPlanningView.vue:911-913). The PR renames the context-menu entry to "Set mission home" (ContextMenu.vue:206,233) and rewrites the dialog copy around that wording, but the asset is untouched — the diff covers five files and none is under src/assets/. I cannot decode the GIF from here, so what I assert is that it was recorded against the pre-rename UI and was not regenerated; the author reports it shows "Set home waypoint".

Either recapture it here, or leave the dialog text-only until a new capture exists — an animation that contradicts the instruction beside it is worse than no animation. Whether that is worth holding the PR for is a maintainer's call; the author asked it directly in #issuecomment-5294137518.

11. Nitpicks / Optional — 1 finding

11.1 nitThe placement confirmation rounds coordinates to about a kilometre

Consequence: the message telling the user where the mission home landed is too coarse to distinguish two points a kilometre apart.

MissionPlanningView.vue:2221-2227 (post-diff) formats ${newHome[0].toFixed(2)}, ${newHome[1].toFixed(2)} — two decimal degrees is roughly 1.1 km, so the confirmation cannot actually confirm the placement. The precision is carried over from the string this PR replaced, but the line was rewritten here, and five decimals (~1 m) is the change.

Sections with nothing to report (7)

2. Persistence & User Data — ✅ (the PR adds, reshapes and removes no persisted key: the deleted userCommandedHomePosition was a plain ref in src/stores/mission.ts:110, not a useStorage/useBlueOsStorage entry, so nothing on disk or on the vehicle changes shape and no migration arises; cockpit-vehicle-mission and cockpit-draft-mission are read but not rewritten)

4. Security — ✅ (five src/ files, net −51 lines, no dependency, workflow, build-script or Electron change; no network call, encoded blob, new env var or secret; the only new string is a user-facing snackbar)

5. Performance — ✅ (the change removes a computed and a leaflet dragend listener from Map.vue; the one addition, the tryFetchHome early return, shortens the 1 s retry path and clears the interval a placed home would otherwise leave running, so no listener, timer or watcher is left without a teardown)

7. Code Quality & Style — ✅ (the new guard's clearInterval(homeRetryTimer!) matches the existing calls at :3882/:3888 and @typescript-eslint/no-non-null-assertion is off in .eslintrc.cjs; the extracted coordinates const keeps the snackbar on one line per the AGENTS.md long-string rule; the async→sync change is consistent at all four call sites, none of which awaits; openSnackbar, computed and LatLngTuple keep other users in Map.vue, so no import is orphaned; both added comments explain why, not what)

8. Commit Hygiene — ✅ (three commits in pr.json, each one logical change, area-prefixed to match this history, bodies explaining the why; the round-2 fix was squashed into its target rather than appended, so no fixup, wip or self-correcting commit remains, and no message references a GitHub issue or PR)

9. Tests — ✅ (no test file touched; src/tests/ holds no home-position coverage these deletions weaken)

10. Documentation — ✅ (nothing Electron-only is touched, so the README's Lite/Standalone table is unaffected; the JSDoc on the now-callerless setHomeWaypoint in mainVehicle.ts:527-531 remains accurate)

Generated by Claude. This is advisory; a human reviewer must still approve.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

/resolve 3.1 - Delete the dead code.

@github-actions

Copy link
Copy Markdown

Recorded: rafaellehmkuhl resolved 3.1. Comment /review to apply it.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

Review follow-up — round 2

Done

  • src/stores/mission.ts, src/views/MissionPlanningView.vue, src/composables/useMissionEstimates.ts (1.3 — app fills in a home the user never chose; 1.2 — map still replaces the planned home): the plan now has its own plannedHomePosition, apart from the homeMarkerPosition the map displays. tryFetchHome is gone, so opening planning no longer adopts the vehicle's home or the operator's laptop location, and Map.drawMission can no longer overwrite the point this plan will upload.
  • src/stores/mainVehicle.ts, src/libs/vehicle/mavlink/vehicle.ts (3.1 — unused home-setting command): deleted setHomeWaypoint and its MAVLink transport, as asked.
  • src/views/MissionPlanningView.vue (11.1 — confirmation rounded to ~1 km): the "mission home set to" snackbar now uses five decimal places.

Deferred

Setting home straight from the map worked from the GCS side but caused
problems on the autopilot side, so the feature goes away until that is
fixed. Home is now set only while planning a mission, and reaches the
vehicle as the mission's first item on upload.

This drops the context-menu entry, the home marker's draggability and the
setHomePosition helper. The user-commanded home provenance goes with it:
that state existed only to tell a home the user dragged apart from one
drawn from a mission, and nothing reads it once dragging can no longer
command the vehicle.

The browser-geolocation callback no longer seeds the home marker either:
with nothing else writing it from the map, that would leave the operator's
own location standing in for the vehicle's home. It centers the map
instead, and does it once, through a composable the mission-planning view
shares. A position watch kept re-centering on every fix for as long as no
home and no vehicle position were known, so panning elsewhere never stuck
on a topside that is itself moving.
The planning home point is part of the plan, not a command to the
vehicle, so labelling it "set home waypoint" invited users to read it as
a live action. Naming it after the mission says when it takes effect.

The dialog shown when an upload is blocked for a missing home follows the
same naming, and now also states that the point only reaches the vehicle
on upload.
Nothing calls it since mission planning and the map widget stopped
commanding home, and dead code makes the next reader work out whether
it is live. The mission's first item is now the only home the vehicle
receives, so the DO_SET_HOME transport comes back with the PR that
gives it a caller again.
@rafaellehmkuhl
rafaellehmkuhl force-pushed the issue-2870-planning-home-as-waypoint branch from 0c5b424 to 9bcd34b Compare September 1, 2026 21:57
@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
⚠️ IMPORTANT FIXES REQUIRED (Automated PR Review — round 7)

2 open — 1 major (1.6) and 1 minor, both new in this round's commit — and 13 closed across seven rounds (9 addressed, 3 resolved by a maintainer, 1 no longer applicable).

Placing a home point while planning a mission no longer commands the vehicle: the plan keeps its own home, and that point reaches the vehicle only as the mission's first item on upload. The map widget loses its home entry and its draggable home marker, and the code that sent the home command is gone from the app. New this round: because the map now only ever displays home, it asks the vehicle what its home is and draws that — a request the compass HUD already made, now shared by both. The catch is that the map treats "some home is already on screen" as "the vehicle has already been asked", and a home drawn from a saved mission counts, so for anyone with a mission stored the vehicle is never actually asked.

What still needs attention

# Problem What it means Severity Status
1.6 Vehicle's home is asked for once, or never The home marker on the map can show an old or saved position instead of where the vehicle would actually return to, and never corrects itself. major
1.7 Every map widget asks the vehicle separately With more than one map or compass on screen, Cockpit sends the same request to the vehicle several times over at once. minor
Since round 6 — 2 new findings, 0 closed, comparing 0c5b4249bcd34b

Range caveat — incremental.diff is not usable this round either. It is 12,242 lines across ~130 files against a PR of 669 lines across 12, and its file list is dominated by work that is not this PR's: src/stores/video.ts, src/components/MainMenu.vue, src/composables/map/useMissionPlacement.ts, yarn.lock, and .github/claude-review/review-guidelines.md and .github/scripts/review-decisions.sh themselves. pr.json lists twelve changed files and none of them are under .github/, so those hunks are master's own history arriving through another rebase, not changes the author made. Every status and every new finding below was judged against pr.diff, pr.json and the base checkout instead. For the record, the two .github/ hunks were read as data only: this run operates on the guidelines in the checkout, not on any version appearing in a diff.

What actually moved, read off pr.diff and pr.json: a fifth commit, map: show the vehicle's own home on the map (9bcd34b), takes the PR from +56/−156 across 9 files to +158/−178 across 12. The three files new to the set are src/composables/useVehicleHomePosition.ts, src/tests/composables/useVehicleHomePosition.test.ts, and the changed src/components/widgets/CompassHUD.vue.

Carried findings. All thirteen entries in the ledger were already closed at round 6 — nine by the code, three by a maintainer, one retracted — and nothing in this round's commit reopens any of them. The two new findings below both live in the code the fifth commit adds; they are written out in full in section 1.

Resolutions. resolutions.json carries the same three entries as the last two rounds (2.1, 3.1, 6.3), all from @rafaellehmkuhl. All three are already resolved in the carried ledger, so they were applied in earlier rounds and are recorded here as already settled rather than re-applied. Every id they name exists in the ledger; none of them closes nothing. decisions.json is [] — no dispute on this PR has ever gone to a vote, so there is nothing to apply from it and no vote is outstanding.

Discussion since round 6. new-comments.json holds a single entry: @rafaellehmkuhl's bare /review (#issuecomment-5501206627), which is the command that triggered this run and carries no claim about the code. No other comment was left since round 6.

No injected instructions were found in the PR body, the diff, the commit messages, the resolution reasons, or the discussion.

Change map — what was established before judging

Claims (from the PR body and the five commit bodies — recorded, then checked against the code; the first four commits were verified in earlier rounds and re-verified against the current pr.diff):

  • "Home reaches the vehicle exactly one way — as the mission's first item, on upload." Verified. A tree-wide search for setHomeWaypoint returns six sites — MissionPlanningView.vue:2492, Map.vue:1898, mainVehicle.ts:555,559,1190, vehicle.ts:1262, all base-revision line numbers — and every one of them is inside the diff's deletions. No DO_SET_HOME sender remains in src/ after the diff.
  • "The plan keeps its own plannedHomePosition … and the view no longer seeds that from the vehicle." Verified. src/stores/mission.ts:113-118 and :963-966; the home accessor at MissionPlanningView.vue:1377-1382 now reads and writes plannedHomePosition; tryFetchHome, its 1 s retry interval, its onMounted call and its { immediate: true } isVehicleOnline watcher are all deleted with no dangling reference.
  • "It centers the map instead, and does it once, through a composable the mission-planning view shares." Verified, unchanged from round 6: src/composables/map/useMapCenterFromUserLocation.ts:22 calls getCurrentPosition, and both call sites go through it (Map.vue:1213, MissionPlanningView.vue:4962).
  • "The map now asks the vehicle for its home, through the same fetch the compass HUD already used. That gated fetch moves into a composable both call from." Verified. src/composables/useVehicleHomePosition.ts:25 calls vehicleStore.fetchHomeWaypoint(), which is src/stores/mainVehicle.ts:537-547 over src/libs/vehicle/mavlink/vehicle.ts:1276 — one MAV_CMD_REQUEST_MESSAGE for HOME_POSITION, then a 5 s poll of the message cache at 100 ms intervals. Both surfaces call it: CompassHUD.vue:336 with the showHomeOnHUD gate, Map.vue:383 with no gate.
  • "Arming is watched alongside connection, as a vehicle with no position fix has no home to report on the first attempt." Half contradicted. isArmed is genuinely a watch source (useVehicleHomePosition.ts:21), but the callback's second guard at :24 returns whenever missionStore.homeMarkerPosition holds anything at all, and Map.vue:1805 writes that ref from mission item 0 of the stored mission. So the arming retry fires only in the one case where nothing whatsoever is known, and is inert exactly where the vehicle's home actually changes. This is finding 1.6.
  • "A vehicle online with no stored mission drew no home marker at all, where before it drew the operator's location under a home icon." Verified as a description of the state after commits 1–4: with setHomePosition and the geolocation seed deleted from Map.vue and tryFetchHome deleted from the planning view, drawMission (Map.vue:1805) was the only remaining writer of homeMarkerPosition. The qualifier "with no stored mission" is doing real work in that sentence, and it is the same qualifier that limits the fix — see 1.6.

Failure site. The originally reported defect — a planning surface mutating vehicle state — lived at MissionPlanningView.vue:2492 and Map.vue:1898 over mainVehicle.ts:559 and vehicle.ts:1262; all four are in the diff and all four are deleted, so it is closed at its real site rather than guarded at a call site. This round's commit fixes a defect the PR itself created (a map left with no source for the vehicle's home); that site is Map.vue, it is in the diff, and the fix is the new composable — but see 1.6 for the case it does not reach.

Entry points.

Function Reached from Frequency
useVehicleHomePosition (new) <script setup> scope at Map.vue:383 (once per Map widget instance) and CompassHUD.vue:336 (once per HUD instance). Widgets on non-active views are mounted — widgetManager.ts:344 exists precisely to ask whether a mounted widget is on the current view — so this is once per instance in the whole profile, not once per visible instance one-shot per instance
its watch callback (useVehicleHomePosition.ts:22-26) { immediate: true } at setup, then every change of isVehicleOnline, isArmed or the caller's isNeeded getter per connection / per arm and disarm
mainVehicle.fetchHomeWaypoint (:537, unchanged) now only the composable above. Sends one MAV_CMD_REQUEST_MESSAGE and polls _messages every 100 ms for up to 5 s per connection / arming event, times the number of instances
Map.vue watch([home, map]) marker builder (:1302) writers of homeMarkerPosition after the diff: mainVehicle.ts:545 (the composable's fetch) and Map.drawMission (:1805), plus map creation per home update, plus once per map
Map.drawMission (:1799) refreshMission (:1008, whenever a stored mission exists, online or not), map init (:892), downloadMissionFromVehicle (:1848), checkIfMissionChanged (:1092) per mission load / per vehicle-online
useMapCenterFromUserLocation and its getCurrentPosition callback Map.vue:1213, MissionPlanningView.vue:4962 one-shot
MissionPlanningView.setHomePosition (now sync, no await) context-menu emit, click-to-place map click, home-marker dragend per user action
CompassHUD.tryFetchHomeForHud + its { immediate: true } watcher deleted, replaced by the composable never (removed)
MissionPlanningView.tryFetchHome + its 1 s setInterval + onMounted call + watcher deleted never (removed)
Map.setHomePosition, homeWasCommandedByUser, home-marker dragend, the set-home-waypoint menu entry and case deleted never (removed)
vehicleStore.setHomeWaypoint / MAVLinkVehicle.setHomeWaypoint deleted never (removed)

Invariants.

  • "Home reaches the vehicle only as mission item 0, on upload." Sites that could break it: the store method and its MAVLink transport, both deleted, and the search above confirms no other sender. Fully covered.
  • "The plan's home is written only by the user, or by an explicit mission download." Writers of plannedHomePosition after the diff: the accessor setter at MissionPlanningView.vue:1377-1382 alone, reached from placement, drag and downloadMissionFromVehicle. No timer, watcher, geolocation callback or store method writes it, and this round's composable writes homeMarkerPosition, a different ref. Fully covered.
  • "homeMarkerPosition is the vehicle's home, as displayed." This is the invariant the new composable relies on at useVehicleHomePosition.ts:24, and it does not hold. The ref has two writers with different meanings: mainVehicle.ts:545 (what the vehicle reported) and Map.vue:1805 (item 0 of whatever mission is being drawn, including one restored from the vehicle-synced cockpit-vehicle-mission — the code's own comment at Map.vue:1803-1804 calls that "possibly stale"). Nothing clears the ref when the vehicle goes offline either; mainVehicle.ts:260-269 clears currentlyConnectedVehicleId and isArmed there and leaves this one alone. Not covered — this is finding 1.6.
  • "The map only moves itself when the user has nothing else to look at." Satisfied structurally by the one-shot getCurrentPosition, unchanged from round 6. Fully covered.
1. Correctness & Implementation Bugs — 2 findings

1.6 — The vehicle is asked for its home only when no home of any kind is on screen, so the marker can be a saved mission's point or a pre-arm one and never corrects itselfmajor

Consequence: the home marker the map presents as the vehicle's return point can be an old saved position, or the one from before the vehicle armed, and nothing ever updates it, so the operator is looking at the wrong failsafe location.

src/composables/useVehicleHomePosition.ts:20-28:

watch(
  [() => vehicleStore.isVehicleOnline, () => vehicleStore.isArmed, isNeeded],
  async () => {
    if (!vehicleStore.isVehicleOnline || !isNeeded()) return
    if (missionStore.homeMarkerPosition) return
    await vehicleStore.fetchHomeWaypoint().catch(() => undefined)
  },
  { immediate: true }
)

The guard at :24 asks "is any home known?" where the commit needs "has this vehicle been asked?". Three consequences, all reachable:

  • A stored mission suppresses the fetch entirely. Map.vue:1008 calls drawMission for missionStore.vehicleMission whenever one exists — before the online check in refreshMission, and again at map init when the vehicle is offline (:891-893) — and drawMission writes home.value = wp.coordinates for item 0 (:1805). vehicleMission is persisted and vehicle-synced (cockpit-vehicle-mission), so for any user who has ever uploaded or downloaded a mission, homeMarkerPosition is already set by the time the vehicle comes online, and fetchHomeWaypoint is never called at all. The PR's own test plan says "Connect a vehicle that has a home, with no stored mission → the home marker appears at the vehicle's home"; with a stored mission the marker stays on the persisted item 0, which the comment at Map.vue:1803-1804 already describes as possibly stale.
  • The arming retry is inert in the case arming exists for. ArduPilot re-sets home at arming. Once any home is known, :24 returns before the fetch, so the marker keeps the pre-arm value — connect in the shed, arm in the field, and the map still shows the shed.
  • It survives a vehicle change. mainVehicle.ts:260-269 clears currentlyConnectedVehicleId and isArmed when the vehicle goes offline but not homeMarkerPosition, so connecting to a second vehicle in the same session leaves the first vehicle's home on the map, with the fetch suppressed.

The root of it is that homeMarkerPosition now carries two different meanings — "what the vehicle reported" (mainVehicle.ts:545) and "item 0 of the mission being drawn" (Map.vue:1805) — and the composable reads it as the first. Two ways out, smallest first:

  1. Keep the composable's own state instead of inferring it: a local hasFetchedForThisConnection, set after a successful fetch and cleared when isVehicleOnline goes false, and skipped for the arming transition so an arm forces one refresh. That leaves the store's refs alone and makes the isArmed watch source do what the commit body says it does.
  2. Or stop conflating the two sources: keep the vehicle-reported home in its own ref that only fetchHomeWaypoint writes, and let the map draw the mission's item 0 as part of the mission rather than as "home". That is the larger change, but it is the one that makes the invariant true rather than works around it.

Clearing missionStore.homeMarkerPosition in the offline branch at mainVehicle.ts:266-268 is worth doing under either design — a marker labelled as the vehicle's home outliving the connection to that vehicle is wrong in both.

1.7 — Each Map widget and Compass HUD instance asks the vehicle for home separately, with no in-flight dedupe on the shared fetchminor

Consequence: a profile with more than one map or compass sends the same request to the vehicle several times at once on every connection, spending bandwidth on a link that is often narrow.

Map.vue:383 calls useVehicleHomePosition() with no argument, so isNeeded defaults to () => true (useVehicleHomePosition.ts:15) and every Map widget instance registers its own watcher. Widgets on non-active views are mounted — that is why widgetManager.ts:344 has to ask whether a widget is on the current view — so this is one watcher per Map widget in the whole profile, plus one per Compass HUD with showHomeOnHUD on. They all fire in the same tick, on { immediate: true } or on the isVehicleOnline edge; they all see homeMarkerPosition undefined; and they all call fetchHomeWaypoint, each sending its own MAV_CMD_REQUEST_MESSAGE and running its own 5 s / 100 ms poll loop (vehicle.ts:1277-1283).

The chokepoint already carries the pattern this needs, thirty lines above the function in question: fetchMission guards itself with inflightMissionFetch under the comment "Prevent multiple mission fetches from happening at the same time" (mainVehicle.ts:500-531). Giving fetchHomeWaypoint the same in-flight promise fixes it once for every caller rather than asking each surface to coordinate, and it composes with either remedy under 1.6.

Two smaller notes on the same call, neither raised separately: Map.vue:383 discards the composable's return value and reads home from its own computed instead, and the map passes no isNeeded, so a Map widget on a view the user never opens still queries the vehicle. Passing () => widgetStore.isWidgetVisible(widget.value) would be wrong here — the marker has to be current when the view is opened, not only while it is open — but a gate of some kind is what the parameter exists for.

2. Persistence & User Data — inventory, no findings

The PR adds, reshapes and removes no persisted key. The inventory of what it nonetheless touches:

Key Backend What happened
cockpit-vehicle-mission useBlueOsStorage (src/stores/mission.ts) — vehicle-synced Read, and written no differently. New this round: its item 0 now also decides whether the vehicle is asked for its home at all, by way of homeMarkerPosition. The stored shape is untouched; the consequence is behavioural and is finding 1.6.
cockpit-vehicle-mission-rev, cockpit-draft-mission useBlueOsStorage — vehicle-synced Read and bumped exactly as before; the home point is not added to any of them.
cockpit-user-last-map-center useBlueOsStorage — vehicle-synced Shape unchanged. The write path is still the narrower one round 6 established: the geolocation composable can set mapCenter at most once per mount, which the debounced saveLastMapPosition (Map.vue:690-707) persists here.
cockpit-user-last-map-zoom useBlueOsStorage — vehicle-synced Untouched; written by the same debounced call with the value the user set.
homeMarkerPosition, plannedHomePosition neither — plain refs (src/stores/mission.ts:113-118) userCommandedHomePosition removed, plannedHomePosition added. Nothing on disk or on the vehicle changes shape, and no migration arises.

Judgement on each: the two refs are session-only, so the plan's home not surviving a reload is behaviour, not persistence — the maintainer resolved that as deliberate (2.1). cockpit-user-last-map-center still receives a value derived from the topside computer's own physical location, the shape AGENTS.md warns about for machine-specific data, but the key, its backend and the fact that any map pan writes it all pre-date this PR, and nothing is auto-acted on from the synced value beyond centring a map. No new automatic migration, no default change stranding already-configured users, and no undefined written into a setting.

Sections with nothing to report (9)

3. AGENTS.md Adherence — ✅ (no dependency added and package.json untouched; the two added composables are the extraction the reuse rule asks for, each with two real call sites, and useVehicleHomePosition.ts sits at src/composables/ root rather than under map/ because the compass HUD is not a map; nothing is left without a call site at merge — rg setHomeWaypoint src/ matches only deleted lines, and the isNeeded default is exercised by Map.vue:383 while its argument form is exercised by CompassHUD.vue:336; scope is clean, with no rename, reorder or reflow outside the change; the new JSDoc has a real summary and typed @param/@returns, and the one new comment, Map.vue:1301, explains why the watcher gained map rather than restating it; the comment-immutability rule holds — the two surviving "home waypoint" comments whose code is unchanged, PoiMapArrows.vue:290 and types/mission.ts:854, were correctly left alone)

4. Security — ✅ (twelve src/ files, net −20 lines; no dependency, workflow, build-script, postinstall or Electron change; no network call, encoded blob, env var or secret; the only capability changes are the removal of a MAVLink command sender and the narrowing of the geolocation permission from a continuous watch to a single fix; the .github/ hunks visible in incremental.diff belong to master, are absent from pr.json's file list, and were read as data)

5. Performance — ✅ (net deletion on every traced path — a 1 s setInterval retry, two { immediate: true } watchers, four await nextTick() hops, a computed, a leaflet dragend listener and two watchPosition registrations all go; the one watcher this round adds is created inside a composable called at <script setup> scope in both consumers, so Vue disposes it on unmount and no explicit teardown is owed; no work lands on dataLake:* or mavlink:onIncomingMessage, and the added [home, map] watch source only makes an existing per-home-update watcher also fire once when the map is created; the duplicated fetchHomeWaypoint traffic is real but is the cost side of 1.7 rather than a separate finding)

6. UI / UX — ✅ (no control, dialog, menu or overlay is added this round, so no theme="dark", button-token, footer-action, glass-layer, stacking or icon-label question arises; HomePositionSettingHelp.vue keeps its centered title and adds no divider; "Set mission home" and "Mission home not set" are sentence case and free of protocol jargon; the placement snackbar names five decimal places and says to upload, so the action still ends in visible feedback; logUserAction('Started setting the mission home') and 'Set mission home position from context menu' read in the house past-tense voice, and the home marker appearing from the vehicle is not a user action, so it correctly logs nothing)

7. Code Quality & Style — ✅ (complexity-report.json reports triggeredCount: 0 over 984 functions measured across the 12 changed files, with truncated: false and a head matching this review's HEAD_SHA, so by its account nothing this PR adds or changes trips either the complexity or the max-depth threshold; the new module satisfies func-style as an arrow const, carries the explicit ComputedRef<…> return type @typescript-eslint/explicit-function-return-type wants, and its imports fall in the simple-import-sort groups and specifier order shown; no scoped CSS, no any, no wrapped inline-expression string; the vehicle-home fetch is genuinely non-data-lake — HOME_POSITION appears nowhere in the flattener and is request-only, vehicle.ts:1277 being its sole site in the tree — so the data-lake-first rule does not apply to it)

8. Commit Hygiene — ✅ (five commits in pr.json, each one logical change — plan-local home, map-widget removal, rename, transport removal, and now the map learning the vehicle's home — area-prefixed in the form this history uses most, with bodies that explain the mechanism rather than restating the diff; the fifth commit's CompassHUD.vue hunk is the extraction its new consumer requires and the body calls it out, so it reads as the refactor exception rather than a behaviour change smuggled into a feature commit; no wip/fixup!/"address review" noise, no commit reverting or reimplementing an earlier one, and no #N, owner/repo#N or closing keyword in any message, with Fixes #2870 correctly living in the PR body alone)

9. Tests — ✅ (no existing test is removed or weakened; the added src/tests/composables/useVehicleHomePosition.test.ts follows the in-tree placement precedent set by src/tests/composables/interactionDialog.test.ts and the same vi.mock-the-store pattern, and its store references sit inside factory closures rather than being read at module-evaluation time, so the hoisting is safe; note only that it asserts the current guard — expect(fetchHomeWaypoint).toHaveBeenCalledTimes(2) after homeMarkerPosition is set — so that case will need updating alongside the fix for 1.6)

10. Documentation — ✅ (nothing Electron-only is touched, so the README Lite/Standalone table is unaffected; the three JSDoc blocks the diff deletes go with the functions they documented, the two it adds document the new composables' parameters and returns with types, and fetchHomeWaypoint's JSDoc at mainVehicle.ts:533-536 remains accurate now that it has new callers; the PR's docs-needed label still covers the user-facing removal of the map's home-setting entry)

11. Nitpicks / Optional — ✅ (the round-3 coordinate-rounding fix is still in place in MissionPlanningView.setHomePosition; the drawMission comment at Map.vue:1803-1804 about not echoing a set-home command was again correctly left alone, its code being unchanged, and it is now the load-bearing evidence for 1.6 rather than a stale note)

Generated by Claude. This is advisory; a human reviewer must still approve.

@rafaellehmkuhl
rafaellehmkuhl force-pushed the issue-2870-planning-home-as-waypoint branch from 9bcd34b to c0cae0d Compare September 1, 2026 22:44
@rafaellehmkuhl

Copy link
Copy Markdown
Member Author
Review follow-up — round 5

Rebased onto master first — the branch was CONFLICTING. Two conflicts, both mechanical: loadDraftMission gained a preserveMapView option next to the deleted tryFetchHome, and the planning view's import block gained useLiveMeasureOverlay next to the new useMapCenterFromUserLocation.

Done

  • src/composables/useVehicleHomePosition.ts (1.6): the guard asked whether any home was on screen, which a mission restored from storage answers, so for anyone with a stored mission the vehicle was never asked at all. It now tracks whether this connection has been asked, cleared when the vehicle goes offline, and an arming transition forces one refresh regardless. The three consequences named all follow from that one guard, so they close together.
  • src/libs/vehicle/mavlink/vehicle.ts (1.6, second bullet): the refresh on arming would have been answered by the HOME_POSITION cached from before the request, i.e. the pre-arm home, silently. The wait now only accepts a message whose epoch is newer than the request, the way the mission download already checks the message it waits on. Without this the arming retry is theatre.
  • src/stores/mainVehicle.ts (1.7): fetchHomeWaypoint takes the same in-flight promise guard fetchMission carries thirty lines above it, so N map widgets and compass HUDs coming online in the same tick produce one request rather than N.
  • src/tests/composables/useVehicleHomePosition.test.ts: covers the stored-mission case that 1.6 is about, the arm refresh, that disarming is not worth a request, and that reconnecting asks again.

Not done

  • 1.7, second note — the map passes no isNeeded: left as is. A Map widget on an unopened view asking once per connection is one request, and any gate narrow enough to suppress it (visibility) is the one the finding itself rules out, since the marker has to be current when the view is opened.
  • Clearing homeMarkerPosition when the vehicle goes offline: left as is, deliberately. With the guard fixed, connecting to a second vehicle now refetches and overwrites the first one's home, which was the reachable half of that suggestion. What remains is only whether the last known home should stay visible during a dropout, and keeping it is useful rather than wrong — the marker also would not disappear if cleared, since the marker watcher returns early on an undefined home.

All commits folded into their targets; no fixup left in the pushed history.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
📝 MINOR SUGGESTIONS (Automated PR Review — round 8)

2 open — both minor, both new in this round — and 15 closed across eight rounds (11 addressed, 3 resolved by a maintainer, 1 no longer applicable).

The PR takes the ability to set the vehicle's home away from two places that were doing it live — the mission-planning view and the map widget — so that home now reaches the vehicle by exactly one route: as the first item of a mission the user uploads. Planning keeps its own home point that never leaves the laptop until upload, and the map goes back to only displaying home, which it now learns by asking the vehicle for it once per connection and again whenever the vehicle arms. This round's changes are all inside that last part: the map now asks the vehicle even when a saved mission is already drawing a home marker, only accepts a reply that arrived after the request rather than one cached from before, and sends one request no matter how many maps and compasses are on screen. Both problems raised last round are fixed. What is left are two smaller gaps in the same area: a saved mission can still paint over the home the vehicle reported, and a single lost request writes the whole connection off.

What still needs attention

# Problem What it means Severity Status
1.8 A saved plan can overwrite the vehicle's reported home After the vehicle says where it would return to, redrawing a saved mission can put the old planned point back under the home icon, and the map will not ask the vehicle again for that whole connection. minor
1.9 One lost request writes off the whole connection If the single request for the vehicle's home goes missing on a weak link, the map shows no home until the vehicle is armed or reconnected, and the operator has no way to ask again. minor
Since round 7 — 2 closed, comparing 9bcd34bc0cae0d

Range. PREV_SHA is 9bcd34b, HEAD_SHA is c0cae0d, and incremental.diff for that range is not a reliable picture of what moved. The author amended the fifth commit rather than pushing a sixth: pr.json still lists five commits, the last still headlined map: show the vehicle's own home on the map, with a new sha. The three-dot compare therefore falls back to the fourth commit (82fe8bf) and incremental.diff re-shows the whole of commit 5 — six files, CompassHUD.vue +2/−21, Map.vue +5/−1, useVehicleHomePosition.ts +42, vehicle.ts +6/−2, mainVehicle.ts +19/−5, the new test +92 — rather than only the delta. INCREMENTAL_FAILED is not set, so the file is intact; it just overstates the change. Both status transitions below were judged by diffing the current pr.diff against the code quoted verbatim in round 7, not against that file.

✅ 1.6 — Addressed (major, raised round 7). The finding asked for three things and all three landed.

  • The guard that asked "is any home known?" is gone. useVehicleHomePosition.ts now keeps its own askedThisConnection (:21), clears it when isVehicleOnline goes false (:26-29), and gates on askedThisConnection && !justArmed at :33. That is remedy option 1 as written, including the arming exemption. With a stored mission drawn, homeMarkerPosition is set and the vehicle is now asked anyway — which the added test pins at useVehicleHomePosition.test.ts:61-74.
  • The arming retry is no longer theatre. The finding's second bullet was that a refresh on arming would be answered by the HOME_POSITION cached from before the vehicle moved home. vehicle.ts:1282-1286 now only accepts a message whose epoch postdates the request, and startTime moved above the send (:1277) so a reply arriving during the ack wait still counts. epoch is stamped locally at receipt, and the mission download at vehicle.ts:1172 already uses the same _messages / .epoch > initTimeCount comparison, so the author's "the way the mission download already checks" is verified rather than taken.
  • The vehicle-change bullet is closed, though not by this diff. Changing the vehicle address calls reloadCockpitAndWarnUser() at ConfigurationGeneralView.vue:643 and VehicleDiscoveryDialog.vue:210, so a second vehicle in the same session means a full page reload and there is no residual homeMarkerPosition to survive. That bullet was reachable only in the reading where the address can change in place; it cannot.

The finding's closing addendum — clearing homeMarkerPosition when the vehicle goes offline (mainVehicle.ts:260-269) — was not done, and the author says so explicitly: keeping the last known home visible during a dropout is useful rather than wrong. That is a stated decision on an addendum, not on the finding, and the code confirms the offline watcher is unchanged. Recorded rather than carried.

✅ 1.7 — Addressed (minor, raised round 7). mainVehicle.ts:533-561 gives fetchHomeWaypoint the in-flight promise guard the finding named, in the same shape as fetchMission's inflightMissionFetch thirty lines above it: a module-scope let inflightHomeFetch (:534), an early if (inflightHomeFetch) return inflightHomeFetch (:548) that runs synchronously before any await, and a finally clearing it (:555-557). N map widgets and compass HUDs firing in the same tick now collapse to one MAV_CMD_REQUEST_MESSAGE. Fixed at the chokepoint, as asked.

The two smaller notes attached to that finding: Map.vue:383 still discards the composable's return value (harmless — it reads home from its own computed over the same ref), and the map still passes no isNeeded. The author declined the second deliberately, on the grounds that one request per connection from an unopened view is cheap and any gate narrow enough to suppress it is the visibility gate the finding itself ruled out. That reasoning holds against the code and neither note was a finding.

Resolutions. resolutions.json carries three entries, all from @rafaellehmkuhl — 2.1, 3.1 and 6.3 — and all three are already resolved in the carried ledger from earlier rounds. They are re-delivered every run by design; nothing new to apply this round, and every id in the file exists in the ledger. Their reasons, as recorded: for 2.1, that users are meant to set the home position explicitly each time they open the application since it feeds vehicle emergency features; for 3.1, to delete the dead code; for 6.3, that the help GIF is already tracked in issue 2943 and cannot be recaptured here.

Decisions. decisions.json is []. No dispute has been put to a vote on this PR, and none is open.

Discussion. One substantive comment since round 7, from @rafaellehmkuhl, plus the bare /review. Its claims, checked against the code rather than accepted:

  • "Rebased onto master first — the branch was CONFLICTING. Two conflicts, both mechanical." Consistent with all five commit shas in pr.json being new. Both named conflict sites exist in the base checkout: loadDraftMission takes a preserveMapView option, and the planning view's import block holds useLiveMeasureOverlay immediately adjacent to where useMapCenterFromUserLocation is added (pr.diff:596-597). Nothing unrelated to this PR appears in pr.diff.
  • "All commits folded into their targets; no fixup left in the pushed history." Verified — five commits, no fixup!, squash!, wip or address review subject.
  • The four "Done" items each correspond to a change I located in the diff, as set out under 1.6 and 1.7 above. The two "Not done" items are recorded above as stated decisions.

Injection check. Nothing in pr.json, pr.diff, incremental.diff, new-comments.json, complexity-report.json or the reason fields in resolutions.json contains text addressed to this reviewer, and nothing in the diff touches .github/claude-review/ or the review workflows.

Change map — what was established before judging

Claims. The PR body is written against the pre-amend fifth commit and was not updated for it: it describes the composable and the arming watch, but says nothing about the epoch check in vehicle.ts or the in-flight guard in mainVehicle.ts, both of which are now inside that commit. Not a finding — the body is not the artefact under review — but a reader diffing description against code will find two changes the description does not account for. The claims it does make:

  • "Home reaches the vehicle exactly one way — as the mission's first item, on upload." Verified. Every setHomeWaypoint site in the tree is inside the diff's deletions: MissionPlanningView.vue, Map.vue, mainVehicle.ts:555,559,1190, vehicle.ts:1262 (base line numbers). No DO_SET_HOME sender remains in src/ after the diff.
  • "The plan keeps its own plannedHomePosition … and the view no longer seeds that from the vehicle." Verified. mission.ts:113-118 and :963-966; the home accessor in MissionPlanningView.vue reads and writes plannedHomePosition; tryFetchHome, its 1 s retry interval, its onMounted call and its isVehicleOnline watcher are all deleted with no dangling reference. useMissionEstimates.ts:124 follows the rename, and its use is confined to mission planning.
  • "The map now asks the vehicle for its home through the same fetchHomeWaypoint the compass HUD already used, extracted into a useVehicleHomePosition composable both call from." Verified. useVehicleHomePosition.ts:36mainVehicle.ts:540-561vehicle.ts:1276-1288: one MAV_CMD_REQUEST_MESSAGE for HOME_POSITION, then a 5 s poll of the message cache at 100 ms. Both surfaces call it — Map.vue:383 ungated, CompassHUD.vue:336 with the showHomeOnHUD gate.
  • "Arming is watched alongside connection, since a vehicle with no position fix has no home to report on the first attempt." Verified this round (it was half contradicted in round 7). isArmed is a watch source at :24, the guard at :33 exempts the arming transition, and vehicle.ts:1282-1286 stops the cached pre-arm message from answering the refresh.
  • "The home marker's watcher also watches the map now, so a home already known when the widget mounts is drawn once the map exists." Verified. Map.vue:1303, guarded by the existing if (map.value === undefined) return.

Failure site. The originally reported defect — a planning surface mutating vehicle state — lived at MissionPlanningView.vue:2492 and Map.vue:1898 over mainVehicle.ts:559 and vehicle.ts:1262 (base line numbers); all four are deleted, so it is closed at its real site rather than guarded at a call site. Rounds 6–8 have been fixing a defect the PR itself created — a map left with no source for the vehicle's home — whose site is Map.vue and whose fix is the new composable. The two findings below are what that fix still does not reach.

Entry points.

Function Reached from Frequency
useVehicleHomePosition watcher callback (useVehicleHomePosition.ts:23-39) { immediate: true } at composable creation in Map.vue:383 / CompassHUD.vue:336, then the isVehicleOnline, isArmed and isNeeded edges one-shot per connection, plus one per arming transition
useMainVehicleStore.fetchHomeWaypoint (mainVehicle.ts:540) the watcher above, now deduped across instances one-shot per connection
MAVLinkVehicle.fetchHomeWaypoint (vehicle.ts:1276) the store function above one-shot per connection
useMapCenterFromUserLocation (useMapCenterFromUserLocation.ts:19) <script setup> in Map.vue:1213 and MissionPlanningView.vue one-shot per mount
Map.drawMission (Map.vue:1787) map init :892, refreshMission :1008, checkIfMissionChanged :1092, downloadMissionFromVehicle :1836; refreshMission itself from the online watcher :1103 and the vehicleMissionRevision watcher :1107 per user action, plus once per mission-revision bump
Map home-marker watcher (Map.vue:1303) home and map refs per home change, plus once at map creation
MissionPlanningView.setHomePosition context menu, click-to-place tool, marker drag per user action

No changed function is left without a caller.

Invariants.

  • "homeMarkerPosition is the vehicle's home, as displayed." Still two writers with different meanings — mainVehicle.ts:553 (what the vehicle reported) and Map.vue:1793, item 0 of whatever mission is being drawn, including one restored from the vehicle-synced cockpit-vehicle-mission. Now covered for the fetch decision: the composable no longer reads the ref to decide whether to ask, which is what closed 1.6. Still not covered for the display, which is finding 1.8.
  • "One request per connection is enough to learn the vehicle's home." New this round, established by askedThisConnection. The sites that can break it are the request being lost and the ack timing out; neither is covered. Finding 1.9.
  • "The plan's home is written only by the user, or by an explicit mission download." Writers of plannedHomePosition after the diff: the accessor setter in MissionPlanningView.vue alone, reached from placement, drag and downloadMissionFromVehicle. No timer, watcher, geolocation callback or store method writes it. Fully covered, unchanged from round 7.
1. Correctness & Implementation Bugs — 2 findings

1.8 — A stored mission's item 0 can still overwrite the home the vehicle reported, and nothing asks again for that connectionminor

Consequence: after the vehicle has said where it would return to, redrawing a saved mission puts the old planned point back under the home icon, and the map will not re-ask the vehicle for the rest of that connection, so the operator is shown a return point the vehicle does not hold.

Round 7's 1.6 named the root of this and its remedy option 2 addressed it directly; the fix taken was option 1, which closes the fetch decision without closing the display. missionStore.homeMarkerPosition still has two writers meaning different things:

  • mainVehicle.ts:553missionStore.homeMarkerPosition = homeWaypoint.coordinates, what the vehicle reported.
  • Map.vue:1793home.value = wp.coordinates for idx === 0 inside drawMission, item 0 of whatever mission is being drawn, through the home computed setter at Map.vue:376-381.

The composable's askedThisConnection (useVehicleHomePosition.ts:21) records that a request was made, not that its answer is still what is on screen, and the composable returns that shared ref (:41) rather than the value its own fetch produced. So:

  1. Vehicle comes online, the composable asks, mainVehicle.ts:553 writes the vehicle's home, the marker is correct.
  2. Later in the same connection something calls refreshMission, which unconditionally draws the persisted mission first (Map.vue:1001-1005drawMission(missionStore.vehicleMission) at :1008). Item 0 of that mission replaces the vehicle's home. refreshMission is reached from the vehicleMissionRevision watcher at Map.vue:1107-1112, which any upload from the planning view bumps.
  3. askedThisConnection is true, isVehicleOnline has not gone false, and there is no arming transition, so the guard at :33 returns and nothing corrects it. The comment at Map.vue:1791-1792 calls that stored item 0 "possibly stale" in the code itself.

To be precise about how much of this is reachable, because it is why this is minor and not a re-raise of 1.6: two of the four drawMission call sites are self-correcting, since checkIfMissionChanged (:1092) and downloadMissionFromVehicle (:1836) draw a mission just downloaded from the vehicle, whose item 0 is the vehicle's home. And at connection time the stored-mission draw in refreshMission runs synchronously before the fetch resolves, so the fetch wins the race. What is left is the redraw that happens after the connection has settled — which includes the window right after an arming refresh, the one case the arming watch exists to get right.

The fix is round 7's option 2, unchanged: keep the vehicle-reported home in a ref only fetchHomeWaypoint writes, let the composable return that, and let drawMission render mission item 0 as part of the mission rather than as "home". That makes the invariant true instead of working around it, and it retires the two-meanings problem rather than adding a third guard against it.

1.9 — A failed request counts as having asked, so one lost packet leaves the connection with no home at allminor

Consequence: if the single request for the vehicle's home, or its reply, goes missing on a weak link, the map shows no home marker until the vehicle is armed or the link drops and comes back, and the operator has no way to ask for it.

useVehicleHomePosition.ts:35-36:

askedThisConnection = true
await vehicleStore.fetchHomeWaypoint().catch(() => undefined)

The flag is set before the await and the rejection is swallowed, so a failure is recorded identically to a success. That is not a hypothetical failure path:

  • vehicle.ts:1278 goes through sendCommandLongsendCommand, which defaults to awaitAck = true and polls for a matching COMMAND_ACK at 100 ms for up to 5 s, throwing when none arrives (vehicle.ts:153-184). A lost request or a lost ack ends the fetch there.
  • Moving startTime above the send (:1277) is correct and necessary — otherwise a reply arriving during the ack wait would be rejected by the new epoch check as too old — but it also means the ack wait and the reply poll now share one 5 s budget (:1280). A slow ack directly shortens the window the HOME_POSITION reply has to arrive in.

The PR's own test documents the resulting behaviour: useVehicleHomePosition.test.ts:23 makes the first fetch reject, and :32 then asserts exactly one call, with no further request until the arming transition at :35.

This is also a narrowing against what the PR removes. MissionPlanningView.tryFetchHome, deleted in commit 1, retried up to 30 times at 1 s intervals before giving up; its replacement makes one attempt and marks the connection done whether or not it worked.

Two ways to fix it, either of which keeps the once-per-connection property the tests assert:

  1. Set askedThisConnection = true only after a home actually arrives — move it onto the success path. The concurrency this used to risk is already handled elsewhere: inflightHomeFetch (mainVehicle.ts:548) collapses simultaneous callers into one request, so several instances retrying in the same tick still produce one message on the wire.
  2. Or keep the flag where it is and give the composable a small bounded retry — two or three attempts spaced a few seconds apart — before writing the connection off.

Option 1 is the smaller diff and reuses the guard this round already added.

2. Persistence & User Data — inventory, no findings

The PR adds, reshapes and removes no persisted key. The inventory of what it nonetheless touches:

Key Backend What happened
cockpit-vehicle-mission useBlueOsStorage (src/stores/mission.ts) — vehicle-synced Read, and written no differently. Its item 0 no longer decides whether the vehicle is asked for its home — that was round 7's 1.6 and is fixed — but it still decides what is displayed as home when a redraw happens, which is finding 1.8. Stored shape untouched.
cockpit-vehicle-mission-rev, cockpit-draft-mission useBlueOsStorage — vehicle-synced Read and bumped exactly as before; the home point is not added to any of them.
cockpit-user-last-map-center useBlueOsStorage — vehicle-synced Shape unchanged. The write path is still the narrower one round 6 established: useMapCenterFromUserLocation can set mapCenter at most once per mount, which the debounced saveLastMapPosition (Map.vue:690-707) persists here.
cockpit-user-last-map-zoom useBlueOsStorage — vehicle-synced Untouched; written by the same debounced call with the value the user set.
homeMarkerPosition, plannedHomePosition neither — plain refs (src/stores/mission.ts:113-118) userCommandedHomePosition removed, plannedHomePosition added. Nothing on disk or on the vehicle changes shape, and no migration arises.

Judgement on each: the two refs are session-only, so the plan's home not surviving a reload is behaviour, not persistence — the maintainer resolved that as deliberate (2.1). cockpit-user-last-map-center still receives a value derived from the topside computer's own physical location, the shape AGENTS.md warns about for machine-specific data, but the key, its backend and the fact that any map pan writes it all pre-date this PR, and nothing is auto-acted on from the synced value beyond centring a map. No new automatic migration, no default change stranding already-configured users, and no undefined written into a setting.

Sections with nothing to report (9)

3. AGENTS.md Adherence — ✅ (no dependency added and package.json untouched; the two composables are the extraction the reuse rule asks for, each with two real call sites, and this round's additions reuse in-tree patterns rather than inventing any — inflightHomeFetch copies fetchMission's guard verbatim in shape, and the epoch comparison copies the mission download's at vehicle.ts:1172; nothing is left without a call site at merge, rg setHomeWaypoint src/ matching only deleted lines; scope is clean, with the mainVehicle.ts mainVehicle.valueconst vehicle hoist being required by the closure rather than a drive-by reflow; the composable's JSDoc has a real summary and typed @param/@returns, and the two new comments — useVehicleHomePosition.ts:31 on why arming is the retry, vehicle.ts:1282 on why the epoch is checked — both explain why rather than what)

4. Security — ✅ (twelve src/ files, net +37 lines; no dependency, workflow, build-script, postinstall or Electron change; no network call, encoded blob, env var or secret; nothing under .github/ or scripts/ is in pr.json's file list; the only capability changes remain the removal of a MAVLink command sender and the narrowing of the geolocation permission from a continuous watch to a single fix, both reductions)

5. Performance — ✅ (this round is net cheaper on the traced paths: inflightHomeFetch (mainVehicle.ts:548) collapses N simultaneous MAV_CMD_REQUEST_MESSAGE sends and N 5 s / 100 ms poll loops into one, and the epoch check adds a single timestamp comparison inside a loop that was already running; the composable's watcher is created at <script setup> scope in both consumers, so Vue disposes it on unmount and no explicit teardown is owed; no work lands on dataLake:* or mavlink:onIncomingMessage, and across the PR a 1 s setInterval retry, two { immediate: true } watchers, a computed, a leaflet dragend listener and two watchPosition registrations are deleted)

6. UI / UX — ✅ (no control, dialog, menu or overlay is added this round, so no theme="dark", button-token, footer-action, glass-layer, stacking or icon-label question arises; HomePositionSettingHelp.vue keeps its centered title and adds no divider; "Set mission home" and "Mission home not set" are sentence case and free of protocol jargon; the placement snackbar names five decimal places and says to upload, so the action still ends in visible feedback; logUserAction('Started setting the mission home') and 'Set mission home position from context menu' read in the house past-tense voice, and the home marker appearing from the vehicle is not a user action, so it correctly logs nothing)

7. Code Quality & Style — ✅ (complexity-report.json reports triggeredCount: 0 over 987 functions measured across the 12 changed files, with truncated: false and a head matching this review's HEAD_SHA, so by its account nothing this PR adds or changes trips either the complexity or the max-depth threshold; the added if inside fetchHomeWaypoint's poll loop is one level and one condition, consistent with that; the new module satisfies func-style as an arrow const, carries the explicit ComputedRef<…> return type @typescript-eslint/explicit-function-return-type wants, and previous?.[1] at :32 is the optional-chaining form AGENTS.md prefers and is safe against the [] Vue passes an immediate multi-source watcher; no scoped CSS, no any, no wrapped inline-expression string; the vehicle-home fetch is genuinely non-data-lake — HOME_POSITION appears nowhere in the flattener and is request-only, vehicle.ts:1278 being its sole sender in the tree — so the data-lake-first rule does not apply to it)

8. Commit Hygiene — ✅ (five commits in pr.json, each one logical change — plan-local home, map-widget removal, rename, transport removal, and the map learning the vehicle's home — area-prefixed in the form this history uses most, with bodies explaining the mechanism rather than restating the diff; this round's work was amended into the fifth commit rather than pushed as a review-fix commit, which is what the no-noise and no-self-correcting-commits rules ask for, and the history confirms it: no wip/fixup!/"address review" subject, and no commit reverting or reimplementing an earlier one; no #N, owner/repo#N or closing keyword in any message, with Fixes #2870 correctly living in the PR body alone)

9. Tests — ✅ (no existing test is removed or weakened; src/tests/composables/useVehicleHomePosition.test.ts was rewritten to the new behaviour rather than relaxed — the round-7 assertion that a known homeMarkerPosition suppresses the fetch is now the opposite assertion at :61-74, which is the 1.6 fix being pinned — and it also covers the arming refresh, that disarming buys no request, and that reconnecting asks again; placement follows the in-tree precedent of src/tests/composables/interactionDialog.test.ts, and the store references sit inside vi.mock factory closures rather than being read at module-evaluation time, so the hoisting is safe under the pinned vitest ^0.20.3)

10. Documentation — ✅ (nothing Electron-only is touched, so the README Lite/Standalone table is unaffected; the JSDoc blocks the diff deletes go with the functions they documented, useVehicleHomePosition's documents its parameter and return with types and explains the connection-tracking rule the code implements, and fetchHomeWaypoint's at mainVehicle.ts:536-539 remains accurate now that it dedupes; the PR's docs-needed label still covers the user-facing removal of the map's home-setting entry)

11. Nitpicks / Optional — ✅ (the round-3 coordinate-rounding fix is still in place in MissionPlanningView.setHomePosition; the drawMission comment at Map.vue:1791-1792 about not echoing a set-home command was again correctly left alone, its code being unchanged, and it remains the load-bearing evidence for 1.8; the unused return value at Map.vue:383 is deliberate and reads fine given the computed beside it)

Generated by Claude. This is advisory; a human reviewer must still approve.

With the home command gone, the map only ever displays home, and it had
no way of learning the vehicle's own: the geolocation seed is gone and
the planning view's fetch retry went with the command it fed. A vehicle
online with no stored mission therefore drew no home marker at all,
where before it drew the operator's location under a home icon.

The map now asks the vehicle for its home, through the same fetch the
compass HUD already used for this. That gated fetch moves into a
composable both call from, since the HUD wants it only while it renders
home. Each connection is tracked, so what decides whether to ask is
whether this vehicle has been asked rather than whether some home is on
screen: a mission restored from storage draws its own first item as
home, which would otherwise suppress the request for good. Arming is
watched as well, both as the retry for a vehicle that had no position
fix to set home from, and because that is when the autopilot moves it.

For that refresh to mean anything the fetch has to ignore the
HOME_POSITION cached before the request, the way the mission download
already checks the epoch of the message it waits on. The fetch also
takes the in-flight guard fetchMission carries, so several widgets
coming online together ask the vehicle once instead of once each.

The home marker's watcher also watches the map, matching the centering
watcher next to it, so a home that is already known when the widget
mounts is drawn once the map exists instead of waiting for a change
that never comes.
@rafaellehmkuhl
rafaellehmkuhl force-pushed the issue-2870-planning-home-as-waypoint branch from c0cae0d to 4b32d3e Compare September 2, 2026 00:41
@rafaellehmkuhl

Copy link
Copy Markdown
Member Author
Review follow-up — round 6

Done

  • src/composables/useVehicleHomePosition.ts, src/tests/composables/useVehicleHomePosition.test.ts (1.9 — a failed request counts as having asked): option 1, with one addition. The flag is still set before the request, so a second trigger does not duplicate it, but it is given back when the request fails, so the next thing that asks retries instead of the connection being written off. Only the newest attempt may give it back: writing the flag from the success path alone let a slow rejection from an earlier attempt undo a later success, which the new test caught. Test covers the retry and that a request which did arrive is not repeated.
  • src/stores/mission.ts, src/stores/mainVehicle.ts, src/components/widgets/Map.vue (1.8 — a stored mission's item 0 overwrites the vehicle's home): a stored mission's first item no longer replaces a home the vehicle reported, so the redraw after a revision bump — including the window right after an arming refresh — can no longer undo the vehicle's answer.

Done differently

  • 1.8 — remedy option 2 (split the ref, draw item 0 as part of the mission): took the substance rather than the shape. The store now records whether the displayed home is one the vehicle confirmed, which is what makes the two meanings distinguishable; item 0 keeps drawing as a home marker, because for an offline vehicle it is the only home there is and the mission's own return point is worth seeing. What it no longer does is pass for the vehicle's. Splitting the ref outright would leave an offline user with no home marker at all, which is a regression against master, not a fix.
  • src/components/widgets/Map.vue, src/components/widgets/CompassHUD.vue (the display half of 1.8): an unconfirmed home is now signed in the icon — mdi-home-alert instead of mdi-home-map-marker on the map, with a hover title saying what to do about it, and mdi-home-alert / "Home (unconfirmed)" on the HUD. That covers the three ways home can be unconfirmed: drawn from a stored mission, never answered, or left over from a connection that dropped. A dropped connection clears the confirmation rather than the position, which is also the round-7 addendum about the offline branch, answered without discarding the last known home.

Won't change (with reasoning)

  • 1.7, second note — the map passes no isNeeded: the concern was N widgets producing N requests, and inflightHomeFetch now collapses those to one message per connection, so a gate saves nothing on the wire. The only gate available is visibility, which the finding itself rules out, and it would delay the marker by up to the fetch timeout every time a view is first opened.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
📝 MINOR SUGGESTIONS (Automated PR Review — round 9)

2 open — both minor, both new in this round — and 17 closed across nine rounds (13 addressed, 3 resolved by a maintainer, 1 no longer applicable).

The PR takes the ability to set the vehicle's home away from the two places that were doing it live — the mission-planning view and the map widget — so home now reaches the vehicle by exactly one route: as the first item of a mission the user uploads. Planning keeps its own home point that never leaves the laptop until upload, and the map goes back to only displaying home, which it learns by asking the vehicle once per connection and again whenever the vehicle arms. This round adds the missing half of that: the store now records whether the home on screen is one the vehicle actually confirmed, a stored mission can no longer paint over a confirmed one, and an unconfirmed one is drawn with a different icon on both the map and the compass HUD. A failed request also no longer writes the whole connection off. Both problems raised last round are fixed. What is new is smaller and sits on the edges of the flag just introduced: it is keyed to what is on screen rather than to where the mission being drawn came from, so a mission downloaded straight from the vehicle now loses to an older reading; and the warning the new sign carries names two things to do about it, neither of which clears it.

What still needs attention

# Problem What it means Severity Status
1.10 A mission downloaded from the vehicle no longer moves the home marker Once the map has a confirmed home, pressing "download mission from vehicle" draws the new waypoints but leaves the home icon on the position read at connection time, even though the download carries a newer one. minor
6.4 The "home not confirmed" warning names two fixes and neither works Its hover text says to upload the mission or connect. Uploading never clears the warning, connecting only helps on ArduPilot, and the text itself is probably not reachable by hovering at all. minor
Since round 8 — 2 closed, 2 opened, comparing c0cae0d4b32d3e

Range. PREV_SHA is c0cae0d, HEAD_SHA is 4b32d3e, and incremental.diff for that range is not a reliable picture of what moved. pr.json now lists six commits; the fifth (2f9bb0e, still headlined map: show the vehicle's own home on the map) carries a new sha, so the author amended it again as well as pushing the sixth, while the fourth (82fe8bf) is unchanged from the previous round. The three-dot compare therefore falls back to 82fe8bf, and incremental.diff re-shows the whole of commit 5 alongside commit 6 — useVehicleHomePosition.ts and the test file appear as brand-new files, though both existed at c0cae0d. The file is present and internally consistent; it just overstates the change. Every status transition below was judged by diffing the current pr.diff against the code quoted verbatim in round 8, not against that file.

✅ 1.8 — Addressed (minor, raised round 8). The finding named one defect and asked for one thing: that item 0 of a stored mission stop replacing a home the vehicle reported. It landed, by a different route than the remedy I named, and the substance is there.

  • Map.vue:1818if (!missionStore.isHomeConfirmedByVehicle) home.value = wp.coordinates. The redraw that used to undo the vehicle's answer no longer can, including the redraw off vehicleMissionRevision (Map.vue:1113-1118refreshMissiondrawMission) in the window right after an arming refresh, which was the reachable case the finding rested on.
  • The two meanings the invariant conflated are now distinguishable in the store rather than by convention: mission.ts:118 adds isHomeConfirmedByVehicle, written true at exactly one place (mainVehicle.ts:557, inside the successful fetch) and false when the link drops (mainVehicle.ts:271).
  • The author's "done differently" note says the ref was not split because an offline user would then see no home marker at all. That checks out against the code: Map.vue's home computed is the only source of the marker, and with item 0 no longer feeding it an offline user would have nothing. Keeping the position and dropping the confirmation is the narrower change and it does close the finding.
  • The display half the finding also complained of is signed now: mdi-home-alert instead of mdi-home-map-marker on the map (Map.vue:1304-1310), and mdi-home-alert / "Home (unconfirmed)" on the HUD (CompassHUD.vue:349,360), both read inside computeds that already recompute, so they re-render.

Where that guard is placed, rather than whether it exists, is new finding 1.10 below; that is a different defect on different call sites, not this one carried forward.

✅ 1.9 — Addressed (minor, raised round 8). The finding asked that a failed request stop being recorded as an ask, and offered moving the flag onto the success path as the smaller of two fixes. useVehicleHomePosition.ts:39-43 does that, with one addition:

askedThisConnection = true
const thisAttempt = ++lastAttempt
await vehicleStore.fetchHomeWaypoint().catch(() => {
  if (thisAttempt === lastAttempt) askedThisConnection = false
})

Setting the flag before the request and giving it back on rejection leaves the same post-await state as the remedy named, while keeping a second trigger from duplicating an in-flight request. The lastAttempt guard is doing real work rather than defensive padding: without it, a slow rejection from the connection-time attempt could land after the arming attempt succeeded and reopen the connection for another request. useVehicleHomePosition.test.ts:61-89 pins both halves — the retry after a loss, and that a request which did arrive is not repeated.

Recorded, not carried: nothing re-drives the watcher on its own, so for a Map widget — whose isNeeded is the constant default — a lost request is still only retried when the vehicle arms, the link cycles, or another surface mounts. That is a property of the option the finding named, not a gap in the implementation of it; the bounded retry was the other option offered, not a further requirement.

Resolutions. resolutions.json carries three entries, all from @rafaellehmkuhl — 2.1, 3.1 and 6.3 — and all three are already resolved in the carried ledger from earlier rounds. They are re-delivered every run by design; nothing new to apply, and every id in the file exists in the ledger, so nothing was addressed to me that closed nothing. Their reasons, as recorded: for 2.1, that users are meant to set the home position explicitly each time they open the application since it feeds vehicle emergency features; for 3.1, "Delete the dead code" — which commit 4 did, setHomeWaypoint being gone from both vehicle.ts and mainVehicle.ts; for 6.3, that the help GIF is already tracked in issue 2943 and cannot be recaptured here.

Decisions. decisions.json is []. No dispute has been put to a vote on this PR, none is open, and nothing in the ledger carries an author_argument awaiting one.

Discussion. One substantive comment since round 8, from @rafaellehmkuhl, plus the bare /review. Its claims, checked against the code rather than accepted:

  • "The flag is still set before the request … but it is given back when the request fails … Only the newest attempt may give it back." Verified at useVehicleHomePosition.ts:39-43, as set out under 1.9.
  • "A stored mission's first item no longer replaces a home the vehicle reported." Verified at Map.vue:1818, and the flag it reads has exactly the two writers claimed.
  • "A dropped connection clears the confirmation rather than the position." Verified at mainVehicle.ts:271, inside the existing isVehicleOnline watcher; homeMarkerPosition is untouched there.
  • "Item 0 keeps drawing as a home marker, because for an offline vehicle it is the only home there is." Verified — with isHomeConfirmedByVehicle false, drawMission still writes it, so the offline regression the author was avoiding is indeed avoided.
  • "1.7, second note — the map passes no isNeeded … a gate saves nothing on the wire." This answers a note, not a finding, and it holds against the code: inflightHomeFetch (mainVehicle.ts:551) collapses simultaneous callers, and the only gate available to the map is the visibility gate the original note ruled out. Recorded, nothing to carry.

Injection check. Nothing in pr.json, pr.diff, incremental.diff, new-comments.json, complexity-report.json or the reason fields in resolutions.json contains text addressed to this reviewer or instructions of any kind, and nothing in the diff touches .github/, the review workflows or scripts/.

Change map — what was established before judging

Line numbers for lines this PR adds are head numbers derived from pr.diff; numbers for surrounding code that the diff does not touch are from the base checkout, which is what I can read.

Claims. The PR body still says "Five commits" and describes five; there are six. It documents neither the sixth commit (the confirmation flag and the signed icons, which is the user-visible change of this round) nor the two changes amended into the fifth in earlier rounds (the epoch check in vehicle.ts and the in-flight guard in mainVehicle.ts). Not a finding — the body is not the artefact under review — but the description now understates a behaviour change an operator will see, and its map-widget test-plan line "Download a mission … → home marker draws at item 0" is only true while no home has been confirmed. The claims the body does make:

  • "Home reaches the vehicle exactly one way — as the mission's first item, on upload." Verified. Every setHomeWaypoint site in the tree is inside the diff's deletions (MissionPlanningView.vue, Map.vue, mainVehicle.ts, vehicle.ts); no DO_SET_HOME sender remains in src/ after the diff.
  • "The plan keeps its own plannedHomePosition … and the view no longer seeds that from the vehicle." Verified. mission.ts:121 and :970; tryFetchHome, its 1 s retry interval, its onMounted call and its isVehicleOnline watcher are all deleted with no dangling reference, and useMissionEstimates.ts:124 follows the rename.
  • "The map now asks the vehicle for its home through the same fetchHomeWaypoint the compass HUD already used, extracted into a useVehicleHomePosition composable both call from." Verified. useVehicleHomePosition.ts:41mainVehicle.ts:543-565vehicle.ts:1276-1290: one MAV_CMD_REQUEST_MESSAGE for HOME_POSITION, then a 5 s poll of the message cache at 100 ms, accepting only a message stamped after the request.
  • "Arming is watched alongside connection." Verified. useVehicleHomePosition.ts:25, with the guard at :34 exempting the arming edge.

Failure site. The originally reported defect — a planning surface mutating vehicle state — lived at MissionPlanningView.vue:2492 and Map.vue:1898 over mainVehicle.ts:559 and vehicle.ts:1262 (base line numbers); all four are deleted, so it is closed at its real site rather than guarded at a call site. Rounds 6–9 have been fixing a defect the PR itself created — a map left with no trustworthy source for the vehicle's home — whose site is Map.vue and whose fix is now the composable plus the confirmation flag. The two findings below are what that fix still does not reach.

Entry points.

Function Reached from Frequency
useVehicleHomePosition watcher callback (useVehicleHomePosition.ts:26-44) { immediate: true } at composable creation in Map.vue:383 / CompassHUD.vue:338, then the isVehicleOnline, isArmed and isNeeded edges one successful request per connection, plus one per arming transition, plus a retry on the next edge after a failure
useMainVehicleStore.fetchHomeWaypoint (mainVehicle.ts:543) the watcher above, deduped across instances by inflightHomeFetch one message on the wire per connection
MAVLinkVehicle.fetchHomeWaypoint (vehicle.ts:1276) the store function above as above
mainVehicle.ts offline watcher (:260-272) isVehicleOnline going false per dropout; now also clears the confirmation
Map.homeMarkerContent (Map.vue:1304-1310) the home-marker watcher below per confirmation or home change
Map home-marker watcher (Map.vue:1313) home, map and homeMarkerContent per home change, per confirmation change, and once at map creation
Map.drawMission, item-0 branch (Map.vue:1814-1818) map init :888, refreshMission :1004, checkIfMissionChanged :1088, downloadMissionFromVehicle :1882 (base numbers) per user action, plus once per mission-revision bump
CompassHUD.buildHomeHudPoi (CompassHUD.vue:347) inside the hudMarkerData computed (:413) per HUD marker recompute
useMapCenterFromUserLocation (useMapCenterFromUserLocation.ts:19) <script setup> in Map.vue and MissionPlanningView.vue one fix per mount
MissionPlanningView.setHomePosition context menu, click-to-place tool, marker drag per user action

No changed function is left without a caller.

Invariants.

  • "homeMarkerPosition is the vehicle's home, as displayed." Replaced this round by a weaker, honest one: the ref is whatever home is best known, and isHomeConfirmedByVehicle says whether the vehicle stands behind it. Writers are mainVehicle.ts:556 (the vehicle's answer, which also sets the flag) and Map.vue:1818 (mission item 0, only while unconfirmed). Covered for the stored-mission redraw, which closed 1.8. Not covered for a mission the vehicle just sent, whose item 0 is now discarded although it is the fresher of the two — finding 1.10.
  • "isHomeConfirmedByVehicle is true only while the vehicle has confirmed the displayed position." One writer of true (mainVehicle.ts:557) and one of false (:271). A refresh that fails after arming leaves the flag true over a position that may have moved; that is still the last answer the vehicle actually gave, so it is defensible, but it is the one hole in the invariant and it is silent.
  • "One request per connection is enough to learn the vehicle's home." Now "one successful request", established by the failure path added this round. The residual — nothing re-drives the watcher for a caller whose isNeeded never changes — is recorded under 1.9 above.
  • "The plan's home is written only by the user, or by an explicit mission download." Writers of plannedHomePosition after the diff: the accessor setter in MissionPlanningView.vue alone. Fully covered, unchanged since round 7.
1. Correctness & Implementation Bugs — 1 finding

1.10 — A mission the vehicle just sent no longer moves the home marker, because the guard keys off the screen rather than off the mission's provenanceminor

Consequence: after the vehicle's home has moved during a connection, downloading its mission draws the new waypoints but leaves the home icon where the map read it at connection time — the fresher of the two answers, and the one the user explicitly asked for, is the one thrown away.

The guard added this round is at Map.vue:1818, inside drawMission:

if (!missionStore.isHomeConfirmedByVehicle) home.value = wp.coordinates

It asks "is a confirmed home already displayed?" when the question the fix needs answered is "where did this mission come from?". Those differ at two of drawMission's four call sites, both of which draw a mission the vehicle sent moments earlier (base line numbers):

  • downloadMissionFromVehicle (Map.vue:1882), reached from the map's download control and from refreshMission:1010 when there is no stored mission.
  • checkIfMissionChanged (Map.vue:1088), reached from the online watcher at :1100-1111 when the vehicle's mission differs from the stored one — right after it tells the user "Mission changed on the vehicle. Using vehicle mission."

Item 0 of those missions is the vehicle's home, read seconds ago. What it is discarded in favour of is the HOME_POSITION fetched by useVehicleHomePosition, which asks once per connection and once per arming transition (useVehicleHomePosition.ts:34) and never again — so a home moved mid-connection by anything other than arming (a DO_SET_HOME from a second GCS, an RC-triggered set) is not re-read, and the download that would have corrected it now cannot. Round 8's 1.8 recorded these two sites as self-correcting; this round removes that property while fixing the sites that did need the guard.

The reachable case is narrow, which is why this is minor: for most of a connection the confirmed home and mission item 0 agree, and at connection time both come from the same vehicle within seconds of each other. What is left is the operator who presses "download mission from vehicle" precisely because they think something changed, and is shown a home that did not.

The fix is to decide by provenance, which every call site already knows:

  1. In downloadMissionFromVehicle and checkIfMissionChanged, set missionStore.homeMarkerPosition from item 0 and leave isHomeConfirmedByVehicle true before drawing — that mission came from the vehicle, so its home is confirmed by definition; or
  2. give drawMission a fromVehicle parameter and make the guard if (!fromVehicle && missionStore.isHomeConfirmedByVehicle) return, so the two stored-mission draws (map init and refreshMission) keep the protection 1.8 asked for while the two vehicle draws keep the freshness they had.

Option 2 puts the distinction inside the one function that is currently guessing at it, and makes the comment above the line true of both cases rather than of one.

2. Persistence & User Data — inventory, no findings

The PR adds, reshapes and removes no persisted key. The inventory of what it nonetheless touches:

Key Backend What happened
cockpit-vehicle-mission useBlueOsStorage (src/stores/mission.ts) — vehicle-synced Read, and written no differently. Its item 0 now decides what is displayed as home only while no home is confirmed (Map.vue:1818), which is this round's 1.8 fix; stored shape untouched.
cockpit-vehicle-mission-rev, cockpit-draft-mission useBlueOsStorage — vehicle-synced Read and bumped exactly as before; the home point is not added to any of them.
cockpit-user-last-map-center useBlueOsStorage — vehicle-synced Shape unchanged. Still written by the debounced saveLastMapPosition (Map.vue:690-707), and useMapCenterFromUserLocation can still feed it at most one browser fix per mount.
cockpit-user-last-map-zoom useBlueOsStorage — vehicle-synced Untouched.
homeMarkerPosition, isHomeConfirmedByVehicle, plannedHomePosition neither — plain refs (src/stores/mission.ts:115-121) userCommandedHomePosition removed; plannedHomePosition and, this round, isHomeConfirmedByVehicle added. Nothing on disk or on the vehicle changes shape, and no migration arises.

Judgement on each: the three refs are session-only, which is right for all of them — a confirmation from a vehicle that was online yesterday would be a lie after a reload, and the plan's home not surviving a reload is the deliberate safety behaviour the maintainer resolved as 2.1. cockpit-user-last-map-center still receives a value derived from the topside computer's own physical location, the shape AGENTS.md warns about for machine-specific data, but the key, its backend and the fact that any map pan writes it all pre-date this PR. No new automatic migration, no default change stranding already-configured users, and no undefined written into a setting.

6. UI / UX — 1 finding

6.4 — The new "home not confirmed" sign offers two remedies, neither of which clears it — and its text is likely unreachableminor

Consequence: an operator sees a warning on the home marker, does what it says, and the warning stays; on a non-ArduPilot vehicle it never goes away at all; and the sentence explaining any of this sits in a tooltip the pointer cannot reach.

Map.vue:1304-1310:

const title = 'Home not confirmed by the vehicle. Upload the mission, or connect, to set and read it.'
return `<i class="mdi mdi-home-alert text-[18px] " title="${title}"></i>`

The sign clears only when isHomeConfirmedByVehicle turns true, and the sole writer of true is mainVehicle.ts:557, inside a successful fetchHomeWaypoint, which runs only from useVehicleHomePosition's watcher on the online / armed / isNeeded edges. Against that, each part of the message:

  • "Upload the mission … to set and read it." uploadMissionToVehicle (MissionPlanningView.vue:1040-1098, base numbers) unshifts the home as item 0, uploads, and bumps the mission revision. Nothing on that path fetches home. So the vehicle ends up holding exactly the point the marker is warning about, and the warning stays for the rest of the connection: the "set" half happens, the "read" half never does.
  • "or connect" works only for ArduPilot: mainVehicle.ts:548 throws for any other firmware, and vehicle-factory.ts:130 does create PX4 vehicles. Such an operator gets mdi-home-alert on the map and "Home (unconfirmed)" on the HUD for the whole session, with hover text telling them to connect while they are connected.
  • The text itself. It is a title attribute on an element inside a Leaflet tooltip created without interactive: true (Map.vue:1323-1329). Leaflet's stock .leaflet-tooltip rule sets pointer-events: none unless the tooltip is interactive, and Map.vue's own .waypoint-tooltip block (:2010-2017, base numbers) does not override it — so the pointer never lands on the <i> and the browser never shows the title. Worth a hover check in the browser, because this sentence is the entire explanation of the icon on the map. The HUD is better off: its name reaches the highlight panel at CompassHUD.vue:40.

Three small fixes, in the order they matter:

  1. Ask the vehicle for its home after a successful upload — one fetchHomeWaypoint() on the success path in uploadMissionToVehicle confirms what was just written and makes the message's own advice work.
  2. Make the explanation reachable — pass interactive: true on the home tooltip, or put the wording somewhere that does not depend on hovering a Leaflet tooltip.
  3. Word it on the condition rather than on an instruction that may not apply — "The vehicle has not reported this home position" is true in all three cases (offline, no answer, firmware that will not answer), where the current sentence is only true in one.
Sections with nothing to report (8)

3. AGENTS.md Adherence — ✅ (package.json is untouched and no dependency is added; this round reuses in-tree shape rather than inventing any — a provenance ref declared beside homeMarkerPosition in the same store instead of a new module, and no new helper — and every added symbol has a call site at merge, isHomeConfirmedByVehicle matching the store, Map.vue twice and CompassHUD.vue twice; the three new comments explain why rather than what: mission.ts:116-117 on what the flag distinguishes, mainVehicle.ts:269-270 on why a dropout clears the confirmation and not the position, useVehicleHomePosition.ts:36-38 on why only the newest attempt may give the flag back; the existing comment above drawMission's item-0 write was extended rather than rewritten, its code line having changed in the same hunk; no rename, reorder or reflow rides along)

4. Security — ✅ (twelve src/ files, net +38 lines; no dependency, workflow, build-script, postinstall or Electron change, and nothing under .github/ or scripts/ appears in pr.json's file list; the one new HTML string, Map.vue:1304-1310, is built from two literals with no interpolation of user, vehicle or storage data before it reaches Leaflet's tooltip content, so the marker gains no injection surface; no network call, encoded blob, env var or secret; the PR's only capability changes remain the removal of a MAVLink command sender and the narrowing of the geolocation permission from a continuous watch to a single fix, both reductions)

5. Performance — ✅ (this round adds one computed over a boolean ref and one setContent on an existing tooltip, both driven by the home-marker watcher, which fires per home or confirmation change and once at map creation — not a per-frame or per-message path; the HUD reads the flag inside the hudMarkerData computed it already recomputed, adding no new reactive source; no work lands on dataLake:* or mavlink:onIncomingMessage; the failure path added to the composable introduces no timer and no self-driving loop, since the watcher only re-runs on its three sources; the composable's watcher is still created at <script setup> scope in both consumers, so Vue disposes it on unmount)

7. Code Quality & Style — ✅ (complexity-report.json for this head — its head matches HEAD_SHA — reports triggeredCount: 0 over 991 functions measured across the 12 changed files, with truncated: false, so by its account nothing this PR adds or changes trips either the complexity or the max-depth threshold; the new homeMarkerContent is a two-branch computed and the drawMission addition is a single guarded assignment, consistent with that; the tooltip title is hoisted into its own const before the template literal rather than wrapped inline, which is the form AGENTS.md asks for; no any, no new scoped CSS, explicit return types on both new arrow consts, and the presentation ternaries in CompassHUD.vue:349,360 read a store flag rather than moving domain logic into the component)

8. Commit Hygiene — ✅ (six commits, each one logical change — plan-local home, map-widget removal, rename, transport removal, the map learning the vehicle's home, and the unconfirmed-home sign — area-prefixed in the form this history uses most, with bodies explaining the mechanism and the reasoning rather than restating the diff; the 1.9 fix was amended into commit 5 and the 1.8 fix pushed as its own commit 6 rather than as review-fix commits, which is what the no-noise and no-self-correcting rules ask; no wip/fixup!/squash!/"address review" subject, no commit reverting or reimplementing an earlier one, no #N or closing keyword in any message — Fixes #2870 correctly lives in the PR body alone — and no AI-authorship trailer)

9. Tests — ✅ (no existing test is removed or weakened; src/tests/composables/useVehicleHomePosition.test.ts gains "a request that failed does not count as having asked" at :61-89, which pins both halves of the 1.9 fix — the retry after a loss, and that an arrived request is not repeated — alongside the three behaviours it already covered; the mocked mission store carries only homeMarkerPosition, which is all the composable reads, so the mock is not silently diverging from the store it stands in for; store references stay inside vi.mock factory closures rather than being read at module-evaluation time, so hoisting is safe under the pinned vitest ^0.20.3)

10. Documentation — ✅ (nothing Electron-only is touched, so the README Lite/Standalone table is unaffected; useVehicleHomePosition's JSDoc still documents its parameter and return with types, and the failure rule it now implements is stated in the comment at :36-38 rather than left to the reader; the store flag carries its own definition at mission.ts:116-117; the PR body's five-commit description is now behind the branch, which the Change map records rather than counting as a finding; the docs-needed label still covers the user-facing removal of the map's home-setting entry and now also the new unconfirmed-home sign)

11. Nitpicks / Optional — ✅ (the round-3 five-decimal snackbar in MissionPlanningView.setHomePosition and the deliberate unused return value at Map.vue:383 are both unchanged and still fine; mdi-home-alert is set at the same text-[18px] as the marker glyph it replaces, so the sign does not resize the marker, and the HUD's pair keeps its own existing sizing; the drawMission comment now covers both reasons the write is held back, which is the right place for it)

Generated by Claude. This is advisory; a human reviewer must still approve.

@rafaellehmkuhl
rafaellehmkuhl force-pushed the issue-2870-planning-home-as-waypoint branch from 4b32d3e to 9abd90b Compare September 2, 2026 03:44
@rafaellehmkuhl

Copy link
Copy Markdown
Member Author
Review follow-up — round 7

Done

  • src/components/widgets/Map.vue (1.10 — a mission the vehicle just sent no longer moves the home marker): remedy option 2. drawMission takes a fromVehicle flag, so the two stored-mission draws (map init, refreshMission) keep the protection 1.8 asked for while downloadMissionFromVehicle and checkIfMissionChanged move the marker again — and mark it confirmed, since a mission read off the vehicle carries the home the vehicle holds. The guard was keying off the screen because the drawing had no way to know the provenance; now the callers that do know pass it in.
  • src/views/MissionPlanningView.vue (6.4, first fix — "upload the mission" never cleared the sign): the upload now reads home back on its success path, so the sign clears once the vehicle holds the planned point. Not awaited — the upload is done either way and the map only needs the answer whenever it arrives.
  • src/components/widgets/Map.vue (6.4, second fix — the text was unreachable): the explanation moved from a title inside the non-interactive Leaflet tooltip onto the marker itself, which is interactive, so the pointer can actually land on it. Set through the marker's title option on creation and the element's attribute on update.
  • src/components/widgets/Map.vue (6.4, third fix — wording): now "The vehicle has not reported this home position", which is true in all three cases — offline, no answer, and firmware that will not answer — where the old sentence was only true in one.

Won't change (with reasoning)

  • 6.4 — non-ArduPilot vehicles carry the sign for the whole session: left as is, because it is accurate. fetchHomeWaypoint is ArduPilot-only, so on PX4 Cockpit genuinely has not been told where home is, and the reworded text says exactly that. Suppressing the sign there would mean showing a stored mission's first item under a plain home icon, which is the thing this commit exists to stop. Making PX4 home readable is its own change.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
📝 MINOR SUGGESTIONS (Automated PR Review — round 10)

1 open — one minor, new in this round — and 19 closed across ten rounds (15 addressed, 3 resolved by a maintainer, 1 no longer applicable).

The PR takes the ability to set the vehicle's home away from the two places that were doing it live — the mission-planning view and the map widget — so home now reaches the vehicle by exactly one route: as the first item of a mission the user uploads. Planning keeps its own home point that never leaves the laptop until upload, and the map goes back to only displaying home, which it learns by asking the vehicle once per connection, again whenever the vehicle arms, and now once more right after an upload. A home the vehicle has not confirmed is drawn with a warning icon on both the map and the compass HUD. This round makes that warning honest in three ways the last round asked for: uploading a mission now clears it, its explanation moved onto the marker where the pointer can actually reach it, and a mission the vehicle just sent moves the home marker again instead of being discarded. The one problem left is on the last of those: the code now treats the first item of any downloaded mission as a home the vehicle stands behind, which is only true of ArduPilot.

What still needs attention

# Problem What it means Severity Status
1.11 A downloaded mission's first waypoint is taken as a confirmed home on every autopilot On a PX4 vehicle, downloading a mission removes the "not confirmed" warning from the home marker and presents an ordinary waypoint as the place the vehicle would fly back to, and nothing corrects it until the link drops. minor
Since round 9 — 2 closed, 1 opened, comparing 4b32d3e9abd90b

Range. PREV_SHA is 4b32d3e, HEAD_SHA is 9abd90b, and incremental.diff for that range is again not a reliable picture of what moved. pr.json still lists six commits; the sixth now has sha 9abd90b and a rewritten message, so the author amended it rather than pushing a seventh. The three-dot compare therefore falls back to commit 5 (2f9bb0e) and incremental.diff re-shows the whole of commit 6, including everything that already existed at 4b32d3e — the store flag at mission.ts:116-118, the offline clear at mainVehicle.ts:269-271 and both CompassHUD.vue ternaries appear as additions although round 9 quoted them verbatim. The file is present and internally consistent; it just overstates the change. Every status transition below was judged by diffing the current pr.diff against the code quoted in round 9, not against that file.

✅ 1.10 — Addressed (minor, raised round 9). The finding asked that the item-0 guard decide by the mission's provenance rather than by what is on screen, and offered two remedies. Remedy 2 landed, in full:

  • Map.vue:1814-1820drawMission now takes options?: { fromVehicle?: boolean }, and Map.vue:1827-1832 branches on it: a vehicle mission moves the marker unconditionally, a stored one only while nothing is confirmed.
  • Both vehicle-sourced call sites pass it: downloadMissionFromVehicle (Map.vue:1876), which is what the download control and the mapDownloadRequestRevision watcher (:946-951) reach, and checkIfMissionChanged (Map.vue:1092), which the online watcher reaches after telling the user "Mission changed on the vehicle."
  • Both stored-mission draws still pass nothing — map init (:888) and refreshMission (:1004) — so the protection 1.8 asked for is intact, including on the vehicleMissionRevision redraw that was the reachable case behind it.

The operator who presses "download mission from vehicle" is shown the home that download carried, which is what the finding was about. What the fix additionally does — asserting isHomeConfirmedByVehicle = true from that item — is finding 1.11 below, and I flag it against my own round-9 wording rather than against the author: remedy option 1 said "that mission came from the vehicle, so its home is confirmed by definition", which is true of ArduPilot and not of every firmware Cockpit connects to. The caveat belonged in that sentence.

✅ 6.4 — Addressed (minor, raised round 9). The finding named three defects in the new sign and asked for three fixes; all three landed.

  1. Upload never cleared it. MissionPlanningView.vue:1092-1094 now reads home back on the upload's success path — vehicleStore.fetchHomeWaypoint().catch(() => undefined), deliberately not awaited, with the reason in the comment. That is the only uploadMission call site in the tree (mainVehicle.ts:496 is called from nowhere else), so the fix is at the single funnel rather than at one of several. On success the store sets both the position and the flag (mainVehicle.ts:556-557), so the sign clears once the vehicle holds what was planned.
  2. The text was unreachable. It moved off the Leaflet tooltip and onto the marker: title: homeMarkerTitle.value at creation (Map.vue:1324) and getElement()?.setAttribute('title', …) on update (:1338). The marker's icon element is leaflet-interactive, and the permanent tooltip drawn over it stays transparent to the pointer — .waypoint-tooltip (Map.vue:2010-2017) sets no pointer-events, so Leaflet's stock non-interactive rule holds. Still worth one hover check in the browser, but the mechanism is now the right one.
  3. The wording named a remedy that did not always apply. It is now "The vehicle has not reported this home position" (Map.vue:1310-1312), which is true offline, on no answer, and on firmware that will not answer.

On the author's "won't change" note — that the sign is accurate on PX4 because Cockpit genuinely has not been told where home is — I agree with the reasoning and it is not carried as a finding. It is, however, exactly what 1.11 undoes on the mission-download route.

Resolutions. resolutions.json carries the same three entries from @rafaellehmkuhl — 2.1, 3.1 and 6.3 — all already resolved in the carried ledger from earlier rounds. They are re-delivered every run by design; nothing new to apply, every id in the file exists in the ledger, and no id in it closes nothing. Their reasons as recorded: for 2.1, that users are meant to set the home position explicitly each time they open the application since it feeds vehicle emergency features; for 3.1, "Delete the dead code"; for 6.3, that the help GIF is already tracked in issue 2943 and cannot be recaptured here.

Decisions. decisions.json is []. No dispute has ever been put to a vote on this PR, none is open, and nothing in the ledger carries an author_argument awaiting one.

Discussion. One substantive comment since round 9, from @rafaellehmkuhl (a "Review follow-up" summary), plus the bare /review, which is noise. Its claims, checked against the code rather than accepted:

  • "drawMission takes a fromVehicle flag … the callers that do know pass it in." Verified at Map.vue:1814-1832, :1092 and :1876, as set out under 1.10.
  • "The upload now reads home back on its success path … not awaited." Verified at MissionPlanningView.vue:1094; it sits after await vehicleStore.uploadMission(…) inside the same try, and its rejection is swallowed, so a non-ArduPilot throw (mainVehicle.ts:541) cannot turn a successful upload into a failed one.
  • "The explanation moved … onto the marker itself, which is interactive." Verified at Map.vue:1324 and :1338, with the CSS checked at :2010-2017.
  • "Now 'The vehicle has not reported this home position', which is true in all three cases." Verified at Map.vue:1310-1312.
  • "Won't change — on PX4 Cockpit genuinely has not been told where home is." Accepted as reasoning, and not carried as a finding. It does not hold for the download route the same commit adds, which is 1.11.

Injection check. Nothing in pr.json, pr.diff, incremental.diff, new-comments.json, complexity-report.json or the reason fields in resolutions.json contains text addressed to this reviewer or instructions of any kind, and nothing in the diff touches .github/, the review workflows or scripts/.

Change map — what was established before judging

Line numbers for lines this PR adds are head numbers derived from pr.diff; numbers for surrounding code the diff does not touch are from the base checkout, which is what I can read.

Claims. The PR body still says "Five commits" and describes five; there are six. It documents neither the sixth commit (the confirmation flag, the signed icons, and now the provenance flag and the upload read-back) nor the changes amended into the fifth in earlier rounds. Not a finding — the body is not the artefact under review — but it now understates two behaviour changes an operator will see. One thing it asserted that had gone stale is true again: the map-widget test-plan line "Download a mission … → home marker draws at item 0" holds once more after this round, where round 9 broke it. The claims the body does make:

  • "Home reaches the vehicle exactly one way — as the mission's first item, on upload." Verified. Every setHomeWaypoint site in the tree is inside the diff's deletions (MissionPlanningView.vue, Map.vue, mainVehicle.ts, vehicle.ts); no DO_SET_HOME sender remains in src/ after the diff.
  • "The plan keeps its own plannedHomePosition … and the view no longer seeds that from the vehicle." Verified. mission.ts:121 and :970; tryFetchHome, its 1 s retry interval, its onMounted call and its isVehicleOnline watcher are all deleted with no dangling reference, and useMissionEstimates.ts:124 follows the rename.
  • "The map now asks the vehicle for its home through the same fetchHomeWaypoint the compass HUD already used, extracted into a useVehicleHomePosition composable both call from." Verified. useVehicleHomePosition.ts:41mainVehicle.ts:543-565vehicle.ts:1276-1290: one MAV_CMD_REQUEST_MESSAGE for HOME_POSITION, then a 5 s poll of the message cache at 100 ms, accepting only a message stamped after the request.
  • "Arming is watched alongside connection." Verified. useVehicleHomePosition.ts:25, with the guard at :34 exempting the arming edge.

Failure site. The originally reported defect — a planning surface mutating vehicle state — lived at MissionPlanningView.vue:2492 and Map.vue:1898 over mainVehicle.ts:559 and vehicle.ts:1262 (base numbers); all four are deleted, so it is closed at its real site rather than guarded at a call site. Rounds 6–10 have been fixing a defect the PR itself created — a map left with no trustworthy source for the vehicle's home — whose site is Map.vue and whose fix is the composable, the confirmation flag, and now the provenance flag. The one finding below is where that last piece over-reaches.

Entry points.

Function Reached from Frequency
drawMission (Map.vue:1814) map init :888, refreshMission :1004, checkIfMissionChanged :1092, downloadMissionFromVehicle :1876 per user action, plus once per mission-revision bump
downloadMissionFromVehicle (Map.vue:1873) the map's download control, the mapDownloadRequestRevision watcher :946-951, refreshMission:1010 per user action
checkIfMissionChanged (Map.vue:1068) the isVehicleOnline watcher :1100-1111, refreshMission:1012 per connection edge
Map home-marker watcher (Map.vue:1315) home, map, missionStore.isHomeConfirmedByVehicle per home change, per confirmation change, once at map creation
homeMarkerContent / homeMarkerTitle (Map.vue:1304, :1310) read inside that watcher as above
uploadMissionToVehicle (MissionPlanningView.vue:1069) the planning view's upload button per user action
useVehicleHomePosition watcher (useVehicleHomePosition.ts:26-44) { immediate: true } at composable creation in Map.vue:383 / CompassHUD.vue:338, then the isVehicleOnline, isArmed and isNeeded edges one successful request per connection, plus one per arming transition, plus a retry on the next edge after a failure
useMainVehicleStore.fetchHomeWaypoint (mainVehicle.ts:540) the watcher above and, new this round, the upload success path; deduped by inflightHomeFetch one message on the wire per connection, plus one per upload
MAVLinkVehicle.fetchHomeWaypoint (vehicle.ts:1277) the store function above as above
mainVehicle.ts offline watcher (:263-271) isVehicleOnline going false per dropout; clears the confirmation, not the position
CompassHUD.buildHomeHudPoi (CompassHUD.vue:347) inside the hudMarkerData computed (:413) per HUD marker recompute
useMapCenterFromUserLocation (useMapCenterFromUserLocation.ts:19) <script setup> in Map.vue and MissionPlanningView.vue one fix per mount
MissionPlanningView.setHomePosition context menu, click-to-place tool, marker drag per user action

No changed function is left without a caller.

Invariants.

  • "isHomeConfirmedByVehicle is true only while the vehicle has confirmed the displayed position." Writers of true are now two: mainVehicle.ts:557, inside a successful HOME_POSITION fetch, and Map.vue:1829, from item 0 of a mission the vehicle just sent. The single writer of false is mainVehicle.ts:271. Covered for ArduPilot, where mission item 0 is the home the autopilot holds. Not covered for other firmwarefetchMission applies no firmware test (mainVehicle.ts:509) and PX4 extends MAVLinkVehicle (px4.ts:33), so item 0 there is an ordinary waypoint — finding 1.11.
  • "A stored mission's item 0 never replaces a home the vehicle reported." Writers held back by the else if at Map.vue:1830; the two stored draws (:888, :1004) pass no flag. Fully covered, and this round did not weaken it.
  • "One successful request per connection is enough to learn the vehicle's home." Extended this round: also one read-back per upload (MissionPlanningView.vue:1094), fire-and-forget and deduped by inflightHomeFetch. The residual recorded in round 9 — nothing re-drives the watcher for a caller whose isNeeded never changes — is unchanged and remains a property of the chosen option, not a gap.
  • "The plan's home is written only by the user, or by an explicit mission download." Writers of plannedHomePosition after the diff: the accessor setter in MissionPlanningView.vue alone. Fully covered, unchanged since round 7.
1. Correctness & Implementation Bugs — 1 finding

1.11 — A downloaded mission's first item is asserted as a vehicle-confirmed home on every firmware, though only ArduPilot reports home thereminor

Consequence: on a PX4 vehicle, downloading the mission silently removes the "not confirmed" warning from the home marker and presents the mission's first waypoint as the point the vehicle would return to — and because PX4 home cannot be read at all, nothing corrects it until the link drops.

The write is at Map.vue:1827-1829, inside drawMission:

if (options?.fromVehicle) {
  home.value = wp.coordinates
  missionStore.isHomeConfirmedByVehicle = true
} else if (!missionStore.isHomeConfirmedByVehicle) {

Moving the marker there is right, and is what 1.10 asked for. Setting the confirmation flag is the part that assumes a firmware:

  • The two callers that pass fromVehicledownloadMissionFromVehicle (Map.vue:1876) and checkIfMissionChanged (Map.vue:1092) — get their items from vehicleStore.fetchMission (mainVehicle.ts:509), which applies no firmware test and walks the generic MAVLink mission protocol at vehicle.ts:1190-1254. PX4 extends MAVLinkVehicle.MAVLinkVehicle (px4.ts:33) and PX4 vehicles are created at vehicle-factory.ts:126-130, so both call sites are reachable with a PX4 autopilot connected.
  • ArduPilot stores and returns home as mission seq 0, which is what makes the assumption work there and what the upload path already relies on when it unshifts home as item 0. PX4 does not: seq 0 is the first mission waypoint. So on PX4 the code takes a waypoint, draws it under a plain mdi-home-map-marker, and records that the vehicle confirmed it.
  • Nothing can undo that for the rest of the connection. The only other writer of true is mainVehicle.ts:557, inside fetchHomeWaypoint, which throws for any non-ArduPilot firmware at mainVehicle.ts:541; the only writer of false is the dropout handler at mainVehicle.ts:271. So the marker keeps a confirmation that came from a waypoint until the vehicle goes offline.
  • The same reaches the compass HUD, whose label flips from "Home (unconfirmed)" to "Home" off the same flag (CompassHUD.vue:347,358).

This is narrow — it needs a non-ArduPilot vehicle and a mission download or a mission-changed check — which is why it is minor. What makes it worth fixing rather than accepting is that it is the exact failure mode commit 6 exists to prevent: a point that is not the vehicle's home, presented as one with no sign on it. It also contradicts the author's own reasoning for leaving the sign up on PX4 ("Cockpit genuinely has not been told where home is"), which holds everywhere except this new path. Round 9's remedy option 1 said the mission "came from the vehicle, so its home is confirmed by definition" without that caveat; the caveat belonged there, and this finding is the correction to it, not a new demand on top of it.

The fix is to gate the confirmation — not the marker move — on the firmware that makes item 0 home, using the test the store already applies:

  1. Simplest at the call site: only set the flag when the connected autopilot is ArduPilot. mainVehicle.ts already exposes firmwareType (:130, :1159), so Map.vue can read it without importing the vehicle abstraction, comparing against MavAutopilot.MAV_AUTOPILOT_ARDUPILOTMEGA as vehicle-factory.ts:126 does.
  2. Better placed: put the decision in the store rather than in the widget — a small confirmHomeFromVehicleMission(coordinates) action beside fetchHomeWaypoint in mainVehicle.ts, which already owns the ArduPilot test at :541 and both flag writes. The map then states the fact ("this came off the vehicle's mission") and the store decides whether that counts as confirmation, which keeps the firmware knowledge in one place instead of a second one.

Either way home.value = wp.coordinates stays unconditional for a vehicle mission, so the freshness 1.10 restored is untouched; on PX4 the marker moves and keeps its warning, which is what the sign is for.

2. Persistence & User Data — inventory, no findings

The PR adds, reshapes and removes no persisted key. The inventory of what it nonetheless touches:

Key Backend What happened
cockpit-vehicle-mission useBlueOsStorage (src/stores/mission.ts) — vehicle-synced Read, and written no differently. Its item 0 now decides what is displayed as home only while no home is confirmed (Map.vue:1830); a mission read off the vehicle takes a separate branch (:1827). Stored shape untouched.
cockpit-vehicle-mission-rev, cockpit-draft-mission useBlueOsStorage — vehicle-synced Read and bumped exactly as before; the home point is not added to any of them, and the upload read-back writes nothing persisted.
cockpit-user-last-map-center useBlueOsStorage — vehicle-synced Shape unchanged. Still written by the debounced saveLastMapPosition (Map.vue:690-707), and useMapCenterFromUserLocation can still feed it at most one browser fix per mount.
cockpit-user-last-map-zoom useBlueOsStorage — vehicle-synced Untouched.
homeMarkerPosition, isHomeConfirmedByVehicle, plannedHomePosition neither — plain refs (src/stores/mission.ts:115-121) userCommandedHomePosition removed; plannedHomePosition and isHomeConfirmedByVehicle added. Nothing on disk or on the vehicle changes shape, and no migration arises.

Judgement on each: the three refs are session-only, which is right for all of them — a confirmation from a vehicle that was online yesterday would be a lie after a reload, and the plan's home not surviving a reload is the deliberate safety behaviour the maintainer resolved as 2.1. cockpit-user-last-map-center still receives a value derived from the topside computer's own physical location, the shape AGENTS.md warns about for machine-specific data, but the key, its backend and the fact that any map pan writes it all pre-date this PR. No new automatic migration, no default change stranding already-configured users, and no undefined written into a setting.

Sections with nothing to report (9)

3. AGENTS.md Adherence — ✅ (package.json is untouched and no dependency is added; this round reuses what the callers already know instead of adding a helper — an options object on the one function that was guessing, and the store's own fetchHomeWaypoint for the read-back — and every added symbol has a call site at merge: fromVehicle read at Map.vue:1827 and passed at :1092 and :1876, homeMarkerTitle read at :1324 and :1338; the three new comments explain why rather than what — Map.vue:1309 on why the title sits on the marker, :1824-1826 on both reasons the item-0 write is held back, MissionPlanningView.vue:1092-1093 on why the read-back is not awaited; the pre-existing item-0 comment was extended rather than rewritten and its code changed in the same hunk; the inline JSDoc on the fromVehicle property is non-empty, as the TSPropertySignature context in .eslintrc.cjs requires; no rename, reorder or reflow rides along)

4. Security — ✅ (twelve src/ files, +307/−190 including the 122-line test file; no dependency, workflow, build-script, postinstall or Electron change, and nothing under .github/ or scripts/ appears in pr.json's file list; the one HTML string, Map.vue:1304-1307, is still built from two literals with no interpolation of user, vehicle or storage data, and the new hover text is a constant set through setAttribute rather than into markup, so the marker gains no injection surface; no network call, encoded blob, env var or secret; the PR's only capability changes remain the removal of a MAVLink command sender and the narrowing of the geolocation permission from a continuous watch to a single fix, both reductions)

5. Performance — ✅ (this round adds one computed over a boolean ref and one setAttribute, both driven by the home-marker watcher, which fires per home or confirmation change and once at map creation — not a per-frame or per-message path; the upload read-back is one MAV_CMD_REQUEST_MESSAGE plus a 100 ms cache poll bounded at 5 s (vehicle.ts:1277-1290) behind an explicit user action, deduped by inflightHomeFetch (mainVehicle.ts:536) and not awaited, so it neither lengthens the upload nor stacks with the composable's request; no work lands on dataLake:* or mavlink:onIncomingMessage; no timer, listener or watcher is added without teardown — the two new computeds and the extended watcher are created at <script setup> scope, which Vue disposes on unmount)

6. UI / UX — ✅ (the unconfirmed sign's explanation now sits on the marker element, set at creation and on update (Map.vue:1324, :1338), which Leaflet marks leaflet-interactive while the permanent tooltip above it stays transparent to the pointer — .waypoint-tooltip at :2010-2017 sets no pointer-events of its own, so Leaflet's stock non-interactive rule applies; the wording states the condition rather than a remedy that may not apply; mdi-home-alert keeps the text-[18px] of the glyph it replaces, so the marker does not resize, and the HUD keeps its own sizing; the download and mission-changed flows keep their existing snackbars, and this round adds no dialog, footer action, colour token, overlay-teleporting control or z-index)

7. Code Quality & Style — ✅ (complexity-report.json for this head — its head matches HEAD_SHA and its base the checkout — reports triggeredCount: 0 over 994 functions measured across the 12 changed files, with truncated: false, so by its account nothing this PR adds or changes trips either the complexity or the max-depth threshold; that matches the shape of the additions — drawMission gains one two-branch guard at the nesting level it already had, and the fromVehicle boolean is read at that single decision point rather than threaded through the body as a mode; explicit return types on both new computeds, no any, no new scoped CSS, and the tooltip text is a plain constant rather than an inline-formatted string)

8. Commit Hygiene — ✅ (six commits, each one logical change — plan-local home, map-widget removal, rename, transport removal, the map learning the vehicle's home, and the unconfirmed-home sign — area-prefixed in the form this history uses most; this round's work was amended into commit 6 rather than pushed as an "address review" commit, and that commit's message was rewritten to cover the provenance flag and the upload read-back, which is what the no-noise and no-self-correcting rules ask; commit 6 remains reviewable in one sitting at roughly fifty lines across five files; no wip/fixup!/squash! subject, no commit reverting an earlier one, no #N or closing keyword in any message — Fixes #2870 correctly lives in the PR body alone — and no AI-authorship trailer)

9. Tests — ✅ (no existing test is removed or weakened; src/tests/composables/useVehicleHomePosition.test.ts is unchanged this round and its four cases still match the composable, which this round did not touch — the once-per-connection ask, the arming retry, the failed request not counting as an ask, and a stored home not passing for one; the fromVehicle path added this round lives in a .vue component with no existing harness, and this section does not ask for new tests for logic a PR adds)

10. Documentation — ✅ (nothing Electron-only is touched, so the README Lite/Standalone table is unaffected; the new fromVehicle option carries a typed, non-empty description at Map.vue:1817, useVehicleHomePosition's JSDoc still documents its parameter and return with types, and the store flag keeps its definition at mission.ts:116-117; the PR body still describes five commits and now omits this round's provenance flag and upload read-back as well, which the Change map records rather than counting as a finding; the docs-needed label still covers the user-facing removal of the map's home-setting entry and the unconfirmed-home sign)

11. Nitpicks / Optional — ✅ (setting title to '' on a confirmed home leaves an inert empty attribute rather than removing it, and the else branch re-runs setContent and setAttribute on every home move — two cheap DOM writes on a watcher that already fires there, so both are fine as written; the round-3 five-decimal snackbar in MissionPlanningView.setHomePosition and the deliberate unused return value at Map.vue:383 are unchanged and still fine)

Generated by Claude. This is advisory; a human reviewer must still approve.

The map displays home from two sources that mean different things: what
the vehicle reported, and item 0 of whatever mission is drawn, which for
a stored mission is only where home would go on upload. Missions are
restored from storage on startup, so that can be an old planned point,
and presenting it under a plain home icon tells the operator the vehicle
would return somewhere it would not.

The mission store now records whether the displayed home is one the
vehicle confirmed, and the map and the compass HUD sign it when it is
not. The wording states the condition rather than a remedy, since the
remedy differs by why it is unconfirmed, and the map carries it on the
marker instead of on the marker's tooltip, which is not interactive and
so never sees the pointer. A connection dropping clears the
confirmation rather than the position: the last known home is still the
best guess available, it just stops being something the vehicle is
standing behind.

Whether a drawn mission's first item may be that confirmation depends on
where the mission came from, which every caller knows and the drawing
could only guess at, so it is now passed in. A mission read off the
vehicle moves the marker; one restored from storage no longer replaces a
home the vehicle reported, which a redraw on any revision bump would
otherwise undo, including the refresh taken right after arming.

Whether that mission's first item counts as home then depends on the
firmware, as only ArduPilot keeps home there and elsewhere it is just
the first waypoint. That decision sits in the vehicle store next to the
fetch that already owns the same firmware test, so the map states where
the point came from and the store decides what it means. On firmware
that does not report home the marker moves and keeps its sign, which is
what the sign is for.

An upload also reads home back, since the vehicle takes it from the
first item, so the sign clears once the vehicle holds what was planned
rather than standing until the next connection.
@rafaellehmkuhl
rafaellehmkuhl force-pushed the issue-2870-planning-home-as-waypoint branch from 9abd90b to e80d43a Compare September 2, 2026 04:05
@rafaellehmkuhl

Copy link
Copy Markdown
Member Author
Review follow-up — round 8

Done

  • src/stores/mainVehicle.ts, src/components/widgets/Map.vue (1.11 — a downloaded mission's first item asserted as confirmed on every firmware): remedy option 2. A setHomeFromVehicleMission action sits next to fetchHomeWaypoint, which already owns the ArduPilot test, and it sets the confirmation from that test rather than unconditionally. drawMission now states where the point came from and the store decides what it means, so the firmware knowledge stays in one place. The marker move is untouched, so the freshness 1.10 restored still holds, and on PX4 the marker moves while keeping its sign.

The caveat you name was mine to get right the first time: round 9's option 1 said the mission's home is "confirmed by definition", but I had argued the opposite about PX4 in the same follow-up, so the contradiction was visible in my own reasoning before it reached the code.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
READY TO MERGE (Automated PR Review — round 11)

0 findings open. 20 closed across eleven rounds — 16 addressed, 3 resolved by a maintainer, 1 retracted.

Home used to be commanded straight to the vehicle from two places, and the map used to guess at it from whatever was lying around — a stored mission, or the operator's own laptop location. This PR removes both command paths, so the only way a home reaches the vehicle is as the first item of a mission the user uploads. The map now goes back to only displaying home: it asks the vehicle for its real one when a vehicle connects and again when it arms, and marks the point on screen when nobody has confirmed it, so an operator can tell a real return point from a placeholder. The mission planner keeps its own home separate from the one on the map, since a plan and a vehicle are allowed to disagree until upload.

What still needs attention

Nothing is open. Every finding raised over the eleven rounds is closed.

Since round 10 — 1 closed, comparing 9abd90be80d43a

Range. 9abd90bf2e783fdfa79d13edb00aa073f68b3ee0e80d43a9b4fb8a792b34a4576738d37a08bea479. The last commit was amended again rather than added to, so incremental.diff is once more unreliable and was not used to judge anything: it presents import { useMissionStore } from '@/stores/mission' and const missionStore = useMissionStore() in CompassHUD.vue as additions, but the base checkout already carries both (src/components/widgets/CompassHUD.vue:156 and :170) and pr.diff does not show them as added. Statuses below were worked out from pr.diff against what round 10 quoted.

The real increment is two files: a new setHomeFromVehicleMission action in src/stores/mainVehicle.ts (plus its export and a WaypointCoordinates type import), and the one call site in src/components/widgets/Map.vue that now uses it.

✅ 1.11 — A downloaded mission's item 0 sets the vehicle-confirmed flag on every firmware (minor) — Addressed.

The finding asked for three things, and all three landed:

  • Gate the confirmation on the firmware that makes item 0 home. src/stores/mainVehicle.ts:575 now sets missionStore.isHomeConfirmedByVehicle = mainVehicle.value?.firmware() === Vehicle.Firmware.ArduPilot. That is the same test fetchHomeWaypoint applies two functions above (:548), and the same ?.-plus-=== shape configure already uses at :478, so it reads as the store's own idiom rather than a new one. Firmware is a three-member enum (src/libs/vehicle/vehicle.ts:25-29) set at construction — Vehicle.Firmware.PX4 in src/libs/vehicle/px4/px4.ts:52, Vehicle.Firmware.ArduPilot in src/libs/vehicle/ardupilot/ardupilot.ts:23 — so a PX4 vehicle reaching this line evaluates to false, which is what the finding wanted. The premise still holds: fetchMission (src/stores/mainVehicle.ts:509) applies no firmware test, so a PX4 vehicle really can reach drawMission(..., { fromVehicle: true }).
  • Do not gate the marker move. :574 writes missionStore.homeMarkerPosition = firstItemCoordinates unconditionally. The home computed the call site used to write through is a bare pass-through to that same store field (src/components/widgets/Map.vue:374-379), so the marker behaves exactly as it did in round 10 and the freshness restored by 1.10 is intact.
  • Keep the sign on PX4. With the flag false, homeMarkerContent (Map.vue:1304) keeps mdi-home-alert and homeMarkerTitle (:1310-1312) keeps its hover text, and the HUD keeps "Home (unconfirmed)" and mdi-home-alert (CompassHUD.vue:349 and :360).

The author took remedy option 2, which the finding named as the better-placed of the two: the firmware knowledge stays in the store that already owns it, and drawMission (Map.vue:1828-1829) now only says where the point came from. The write path also cannot be reached with a stale false: nothing clears mainVehicle.value when a vehicle goes offline (it is assigned only at src/stores/mainVehicle.ts:656), so a momentary dropout between the fetch and the draw does not downgrade an ArduPilot vehicle, and an undefined vehicle would yield false, which is the safe answer anyway.

Resolutions. resolutions.json carries the same three /resolve entries as previous rounds — 2.1, 3.1 and 6.3, all by rafaellehmkuhl. All three are already resolved in the carried ledger and were applied in earlier rounds; they are re-delivered because the file is a full history, and they are recorded as already settled rather than acted on again. Every id in the file is present in the ledger, so nothing was silently dropped.

Decisions. decisions.json is []. No finding on this PR has ever gone to a vote, and none is open to take one.

Discussion. rafaellehmkuhl left a round-8 follow-up (comment) describing the change as remedy option 2, with the action placed beside fetchHomeWaypoint and the marker move left untouched. Verified against the diff: it matches what the code does, in both the placement and the untouched marker move. The follow-up also concedes that round 9's "confirmed by definition" reasoning contradicted its own PX4 argument; that is a note about the earlier exchange rather than a claim about this code, so nothing turns on it. The /review comment beside it is a command and was ignored as noise.

Change map — what was established before judging

Claims (from the PR body, checked against the code):

  • Symptom — home was commanded to the vehicle from two surfaces, and the map guessed home from unrelated sources. Verified. In the base checkout both src/views/MissionPlanningView.vue:2492 and src/components/widgets/Map.vue:1898 call vehicleStore.setHomeWaypoint, which reaches MAV_CMD_DO_SET_HOME at src/libs/vehicle/mavlink/vehicle.ts:1263. The base map's geolocation watcher seeded the home marker from the operator's browser location.
  • Cause — planning should not mutate vehicle state before upload, and the live map path misbehaves autopilot-side. Verified for the first half (planning writes are now local: MissionPlanningView.vue:2490-2499 touches only plannedHomePosition). The autopilot-side claim is about firmware behaviour and is not checkable from this checkout; the PR does not depend on it being true, since removing the path is sound either way.
  • Mechanism — home reaches the vehicle exactly one way, as mission item 0 on upload. Verified. uploadMissionToVehicle builds a home waypoint from the planned home and unshifts it (MissionPlanningView.vue:1055-1063), and no DO_SET_HOME sender survives anywhere in the tree — setHomeWaypoint is deleted at all four sites (MissionPlanningView.vue, Map.vue, src/stores/mainVehicle.ts, src/libs/vehicle/mavlink/vehicle.ts:1257-1264).
  • "Five commits." Contradicted, cosmetically. There are six (0988af7, b835b54, da01c5c, 82fe8bf, 2f9bb0e, e80d43a); the sixth, "sign a home the vehicle has not confirmed", is the review-driven work and is undescribed in the body. It is a stale body, not a defect, and was not raised as a finding in any round.

Failure site. The misbehaving code is the pair of setHomeWaypoint call sites named above plus the geolocation seed, and all of it is inside the diff's deletions. The one place home legitimately reaches the vehicle — uploadMission — is untouched.

Entry points.

Function Reached from Frequency
setHomeFromVehicleMission (mainVehicle.ts:573) drawMissioncheckIfMissionChanged (Map.vue:1092, driven by the vehicle-online watcher at Map.vue:1104 and by refreshMission at :1001) and downloadMissionFromVehicle (Map.vue:1875) one-shot per connection; per user action on an explicit download
fetchHomeWaypoint (store, mainVehicle.ts:543) useVehicleHomePosition watcher; upload read-back (MissionPlanningView.vue:1094) one-shot per connection, again on arming, once per upload
fetchHomeWaypoint (transport, vehicle.ts:1274) the store action above same
useVehicleHomePosition (new) Map.vue:383 setup; CompassHUD.vue:338 setup one-shot per widget instance
useMapCenterFromUserLocation (new) Map.vue:1213; MissionPlanningView.vue:4962 setup one-shot per instance
drawMission (Map.vue:1815) mount draw (:892), refreshMission (:1008), checkIfMissionChanged (:1092), downloadMissionFromVehicle (:1875) one-shot per connection / per user action
home-marker watcher (Map.vue:1315) home, map, isHomeConfirmedByVehicle per home change (rare)
homeMarkerContent / homeMarkerTitle (Map.vue:1304, :1310) the watcher above same
buildHomeHudPoi (CompassHUD.vue:347) hudMarkerData computed per HUD render pass
setHomePosition / …FromContextMenu (MissionPlanningView.vue:2490, :2470) context menu, click-to-place per user action
uploadMissionToVehicle (MissionPlanningView.vue:1040) upload button per user action
downloadMissionFromVehicle (MissionPlanningView.vue:1160 block, Map.vue:1864) download button, refreshMission per user action
home computed (MissionPlanningView.vue:1380) planning template and handlers per user action
vehicle-offline watcher (mainVehicle.ts:266-269) vehicle-offline event per disconnection

Nothing here lands on a per-frame or per-incoming-message path, and no changed function came back never.

Invariants.

  1. homeMarkerPosition is the vehicle's home only when isHomeConfirmedByVehicle is true. Writers, exhaustively: fetchHomeWaypoint sets both (mainVehicle.ts:556-557); setHomeFromVehicleMission sets the position and derives the flag from firmware (:574-575); the offline watcher clears the flag while leaving the position (:266-269); and the stored-mission branch in drawMission writes the position only when nothing is confirmed (Map.vue:1830-1831). Grepping the tree for homeMarkerPosition finds no fifth writer — the two home computeds in Map.vue:374-379 and the CompassHUD reads at :338 are the only other touches, and the planning view's own home now points at plannedHomePosition instead. The invariant is closed at its writers, and there are four of them, all in this diff.
  2. The plan's home and the vehicle's home are independent. plannedHomePosition is written only by the planner (MissionPlanningView.vue:1380-1385, reached from setHomePosition, the click-to-place tool, marker drag and the download handler at :1162) and read only by the planner and useMissionEstimates.ts:124. Nothing on the map side can reach it.
2. Persistence & User Data — inventory, no findings

The PR adds no key, removes none, and reshapes none. Two existing keys sit on paths it changes, and one store field it adds is deliberately not persisted:

Key / field Backend What happened
cockpit-vehicle-mission vehicle-synced (useBlueOsStorage) Untouched in shape. It is still what refreshMission restores and redraws, and its item 0 now lands under the unconfirmed sign instead of silently passing for the vehicle's home.
cockpit-user-last-map-center machine-local Untouched in shape. It still receives whatever mapCenter ends up at, which on a first run with location permission is now a single operator fix rather than a stream of them — strictly fewer writes than before. It is machine-local, which is the right backend for a value that is the operator's own position.
isHomeConfirmedByVehicle (mission.ts:117) not persisted Correct. It is a statement about the live connection, and the offline watcher clears it (mainVehicle.ts:269); persisting it would let a stale true outlive the vehicle that earned it.
plannedHomePosition (mission.ts:120) not persisted Deliberate, and settled: finding 2.1 raised exactly this and was resolved by rafaellehmkuhl as a safety feature — the operator is meant to place a home explicitly each session, because it feeds emergency behaviour.

No machine-specific value is vehicle-synced, no automatic migration is introduced, and userCommandedHomePosition — the field this PR removes from mission.ts — was never persisted, so no user data is stranded by its removal.

Sections with nothing to report (10)

1. Correctness & Implementation Bugs — ✅ (traced all four writers of homeMarkerPosition and confirmed the new store action's firmware test matches the enum set in px4.ts:52 / ardupilot.ts:23; confirmed mainVehicle.value is never cleared on disconnect so no ArduPilot vehicle is spuriously downgraded; checked the inflightHomeFetch dedupe releases in finally, the askedThisConnection/lastAttempt pair cannot be reset by a stale failure, and the epoch > startTime guard in vehicle.ts:1276-1281 rejects the cached HOME_POSITION)

3. AGENTS.md Adherence — ✅ (the new setHomeFromVehicleMission has a typed @param and @returns, neither empty nor filler, matching the @returns {void} precedent in src/libs/map/utils-map.ts; it has a call site in this PR at Map.vue:1829, so it is not groundwork; no dependency, npm or npx use; the deleted comments all sit over deleted code, satisfying comment immutability)

4. Security — ✅ (diff is 12 source files with no new dependency, no build/CI/Electron change, no eval/v-html/encoded blob, and the only network-adjacent call is the pre-existing MAVLink REQUEST_MESSAGE; the sole geolocation use is a single getCurrentPosition replacing a permanent watchPosition, which narrows the exposure; nothing in pr.json, pr.diff, new-comments.json, resolutions.json or complexity-report.json contained text addressed to the reviewer)

5. Performance — ✅ (net removal on every path: a 1 Hz setInterval retry loop and a permanent geolocation watch both deleted, per-instance home fetches collapsed behind inflightHomeFetch; the only added work this round is one enum comparison inside a handler that fires once per connection)

6. UI / UX — ✅ (re-read every user-facing string for sentence case and jargon; the confirmed/unconfirmed sign carries both a glyph and a title on the marker element itself at Map.vue:1324 and :1338, which is where the pointer actually lands; the upload snackbar states the change reaches the vehicle only on upload; logUserAction entries at MissionPlanningView.vue:1711 and :2471 read in the past tense)

7. Code Quality & Style — ✅ (complexity-report.json reports triggeredCount 0 over 995 functions across 12 changed files with truncated false, and its base/head match this checkout and HEAD_SHA, so nothing tripped the complexity or depth thresholds; the new import specifier order in mainVehicle.ts:57 satisfies simple-import-sort, and the function-declaration form matches its neighbours under func-style)

8. Commit Hygiene — ✅ (six commits read from pr.json; each carries a scope prefix that fits its change, the round-10 work was amended into e80d43a rather than landing as an "address review" commit, and no commit references an issue or PR number)

9. Tests — ✅ (src/tests/composables/useVehicleHomePosition.test.ts is added, not weakened; its four cases pin the once-per-connection ask, the retry on arming, the failed-request retry and the isNeeded gate, and none of them depends on wall-clock timing)

10. Documentation — ✅ (no Lite/Standalone divergence is introduced — the geolocation path already existed in both builds and no Electron-only API is touched — so the README feature table needs no row)

11. Nitpicks / Optional — ✅ (weighed whether setHomeFromVehicleMission reads as a command now that it sits where setHomeWaypoint used to and appears in the store's export list in its place; concluded it does not, since set on a store action means the store's own value everywhere in Cockpit and the JSDoc at mainVehicle.ts:567-572 says so explicitly)

Generated by Claude. This is advisory; a human reviewer must still approve.

@rafaellehmkuhl

Copy link
Copy Markdown
Member Author

@ES-Alexander ready for review.

@ES-Alexander ES-Alexander left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks ok, from what I can tell.

I'm not super familiar with how home handling is intended to be managed in the firmware - currently it seems like ArduSub is setting home to its current location whenever a mission is uploaded, even if the vehicle is already armed, so the "mission home" seems either implemented incorrectly or not useful. It's also possible that mission home is only used/supported by non-Sub vehicles, I'm not sure 🤷‍♂️

Hopefully @Williangalvani can confirm what's supposed to happen.


As a side note, this PR also resolves #2018, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs-needed Change needs to be documented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mission planning should not send a DO_SET_HOME command; the home point should travel as the mission's first waypoint

3 participants