mapping pipeline: candidate zone editing from the dashboard - #101
Open
MJohnson459 wants to merge 1 commit into
Open
mapping pipeline: candidate zone editing from the dashboard#101MJohnson459 wants to merge 1 commit into
MJohnson459 wants to merge 1 commit into
Conversation
The zone editor shipped against the *canonical* revision, which left the case the mapping pipeline is actually built around unreachable. `segment-map` hands over a map whose rooms are called `zone_01`..`zone_07`; renaming them meant deriving from the published revision, so the placeholders had to be **promoted in order to be allowed to fix them** — publishing a map because it was wrong — and the coordinates were then rebound onto a frame they had not been drawn on. So the edit now names the revision it edits (`POST …/floors/<floor>/zones` takes a `revision`; omitted, it is the canonical one as before), and the editor moves into the review pane that #100 built, which is the only place a candidate's own map is on screen. Editing stays a *derivation*: the named revision's map bytes are re-packed with the submitted zones and accepted as an ordinary candidate, inert until promoted, so nothing stored is ever written and promotion stays the only write that moves a floor. Five things are load-bearing. **A derivation is held to `promote`'s bar, not the upload's.** A revision with no posegraph cannot be extended — an error for a robot's upload, where the session can be re-run, and a *warning* on something already stored, which navigates perfectly and which `promote` accepts. `accept` therefore takes `require_posegraph`. Found in a browser, not by reading: every sim site bundle is such a revision, so `edit zones` sat beside a `promotable` verdict and could only ever fail. **The vocabulary half is editable, and `navigable` is not written when the kind already implies it.** Kind and aliases join name and display_name in each row. Every zone arrives from the server with `navigable` filled in (`zone_term` defaults it from the kind), so writing it back verbatim would carry a `keepout`'s `false` onto a zone just changed to `room` — a room nothing can be dispatched to, with nothing on screen to say why. Empty lists are dropped for the same reason: they say nothing the default does not. **The editor refuses exactly what the robot's loader refuses.** A name a dispatcher cannot type, and two zones answering one query — `ambiguities` mirrors `bundle.ambiguities` (names and aliases, not display names), because the loader *refuses* an ambiguous vocabulary rather than resolving it by dict order, and a stored candidate no robot will load is worse than a rejected save. **A pose can be placed, not only dragged.** A segmented room is a polygon with no `x`/`y`, so it drew no cross to drag and the robot derived a centroid — the middle of the outline rather than where you would send a robot. `⌖` arms the next map click. **After a save the pane selects the new candidate and re-reads it**, so the zones on screen are the saved ones from the server. That retires the MVP's frozen overlay, which existed only because the operations map had nothing to re-render but the stale set the edit was made from (read as data loss, 2026-08-02). Editing is a mode: floor picker, revision list and promote are locked while it is up, there is no autosave, `cancel` discards. One pre-existing defect fell out. The map pane's review button was hidden unless the floor on screen had candidates, and above 760 px the tab bar is hidden too — so the pane built for floors no robot is reporting was reachable only through a floor a robot *was* reporting. It is now always there, and says how many candidates the floor has when it knows. Verified: 13 API tests (`test_zone_edit.py`, incl. deriving from a candidate, a floor with nothing published, the vocabulary revision continuing from the edited revision, an unknown/traversing revision, and the posegraph bar), 50 node tests (the kind list read out of `bundle.py` so it cannot drift, alias round-trip, ambiguity, the `navigable` rule, the editor's home), and four new checks in `browser_check.mjs` — `pixi run fleet-ui-check` passes 30/30 against a real broker, server, fleet and chrome, at desk width and on an emulated phone. Still open on the task: carry-forward accept/reject (needs task 345), and grid snapping / shared vertices (task 350). Claude-Session: https://claude.ai/code/session_01LUGSpEmdmC68oBaWp6B5Qz Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fleet UI: name a candidate's zones before promoting it (commit 262dc44, branch
zone-editor-candidate-edit, worktree .claude/worktrees/zone-editor-346; branched
off origin/main 6464f57 so it sits on top of #100's review pane. Unpushed.)
The MVP (PR #95, merged) edited the canonical revision only, which left the
case the mapping pipeline is built around unreachable:
segment-maphands over amap whose rooms are
zone_01..zone_07, and renaming them required promotingthe placeholders first — publishing a map because it was wrong — besides
rebinding coordinates onto a frame they were not drawn on.
What changed:
POST /v1/sites/<site>/floors/<floor>/zonestakes arevision: the revisionbeing edited (omitted = canonical, as before). Still a derivation — that
revision's map bytes re-packed with the submitted zones, accepted as an
ordinary candidate, inert until promoted; nothing stored is written.
vocabulary_revisioncontinues from the edited revision so a latercarry-forward can tell which naming is newer, and the audit row names the
floor and the revision edited.
place a candidate's own map is on screen. Editing is a mode there (floor
picker, revision list and promote locked; no autosave). After a save the pane
selects the new candidate and re-reads its zones, which retires the MVP's
frozen-overlay workaround — what is on screen is the saved set from the server.
kind(a<select>of zone/v0's kinds) andaliases(one comma-separated field) beside name and display_name.
navigableiswritten only when it deviates from the kind, or a
keepoutchanged toroomwould silently stay undispatchable. The editor refuses client-side exactly
what the robot's loader refuses: a non-dispatchable name, and two zones
answering one query (mirrors
bundle.ambiguities).⌖arms the next map click as a zone's pose — the only way to give one to asegmented room (a polygon with no x/y, whose pose the robot otherwise derives
as a centroid).
Two defects found and fixed on the way, both invisible without a browser:
accept()validated withrequire_posegraph=True(the upload bar) whilepromote()and the review pane use False, soedit zonessat beside a"promotable" verdict and could only ever fail for any revision without a
posegraph — which is every sim bundle and every rsync-seeded floor.
acceptnow takes the flag; a derivation is held to its source's bar, uploads are
unchanged (asserted both ways).
candidates, and above 760 px the tab bar is hidden too — so at desk width the
pane built for floors no robot is reporting was reachable only via a floor a
robot was reporting. Now always present.
Verified: 13 API tests in test_zone_edit.py (derive from a candidate and prove
the candidate's map bytes travel; a floor with nothing published; vocabulary
revision; unknown and path-traversing revision; the posegraph bar both ways),
50 node tests in ui_test.mjs (kind list read out of bundle.py so it cannot
drift, alias round-trip, ambiguity, the navigable rule, editor-lives-in-review,
.zone-rows[hidden]), 256 pytest in mote_fleet/test, pre-commit clean, and fournew checks in browser_check.mjs —
pixi run fleet-ui-checkpasses 30/30 againsta real broker, server, three fake robots and chrome, desk width and emulated
phone. Screenshots from that run are in the job tmp dir (fleet-ui-zones.png
shows the saved candidate selected with its own zones).
Out of scope, still open: carry-forward accept/reject needs task 345 (not built
yet); grid snapping + shared vertices is task 350. The
upload.jsonvalidatorwarning visible on derived candidates is task 348.