Skip to content

Zones are no longer taught only by driving: fix unbound remedies, editor anchor provenance, and the drive-to-teach prose - #112

Merged
MJohnson459 merged 1 commit into
mainfrom
zones-unbound-remedies
Sep 2, 2026
Merged

Zones are no longer taught only by driving: fix unbound remedies, editor anchor provenance, and the drive-to-teach prose#112
MJohnson459 merged 1 commit into
mainfrom
zones-unbound-remedies

Conversation

@MJohnson459

Copy link
Copy Markdown
Contributor

The zone layer was written when a zone got geometry one way: a robot was driven
there and save-zone captured the pose. segment-map and the fleet dashboard's
zone editor have since added two more, and M4 made a binding travel inside a map
revision — so a robot can hold geometry for a floor it has never entered. Five
behaviours and about sixty prose sites still said otherwise.

unbound is a fact about the revision, not about where the robot has been

The goto/fetch refusal, task_server's boot warning, site info and
bundle.validate's warning each gave "drive there and run save-zone" as the
remedy. They now name the mechanism — no geometry for this name in the binding
this robot holds — and all three remedies: place it in the dashboard's zone
editor and promote, pull the revision that binds it, or drive there and
save-zone, which is the one that also measures an approach heading. site info puts the remedy on its own line under the count, since a one-word tally
and three sentences do not belong in one parenthesis.

Geometry an operator places is no longer recorded as one a robot drove to

zone.anchor() defaulted to taught and the editor sent no anchor at all, so
every click, drag and reshape was stored as a measurement — in the one field
that exists to tell a later reader whether to trust a coordinate after the map
changes. The editor now stamps zone/v0's external (EDITOR_ANCHOR, applied
through reanchored) on every zone whose geometry it moves, and leaves an
untouched one carrying what it arrived with, so a segment-map room stays
derived until somebody reshapes it. Renames and notes do not re-anchor:
vocabulary is not geometry.

external is zone/v0's closest fit rather than an exact one — the spec glosses
it as an off-platform localisation system — but the schema's enum is closed, and
the alternatives were recording a click as a measurement (taught) or as an
algorithm's output (derived), which are the two larger lies. A successor
revision should carry a method for it; noted in the code and in CLAUDE.md
against #616.

The browser says only the method and by: zone-editor; bundle_store. _stamp_anchor fills in at from the server's clock and rewrites by to name
the operator holding the token — the half a browser cannot be trusted for.
Carrying the anchor through took two fixes: bundle.parse_zones dropped the
field outright, and zone.split stamped taught over whatever arrived, so
between them the editor's own output was relabelled on the way back in.
zone.read_anchor validates one at the parse, so a method outside the four is
a 422 rather than a stored claim nothing afterwards could tell from a real one.

The snap rule loses its taught justification with it: nothing re-snaps a
coordinate the operator did not touch, whoever put it there, and a dragged pose
does snap, because a drag is a fresh coordinate with the map's precision and
no more.

Prose

CLAUDE.md's "zones are taught by driving there... not by editing YAML" and
"nobody has driven me there", docs/robot/sites.md's teach-by-driving heading,
mote_tasks/README.md's "reachable by construction" and its pre-split YAML
example, save_zone.py's docstring, bundle.binding()'s "never leaves this
robot" (false since M4), and mapping-pipeline.md's click-to-teach listed as
unbuilt (it shipped) are rewritten. About fifty further uses of "taught" as a
synonym for "bound" are now "bound"; it is kept where it means the anchor
method or a floor written before a migration.

Verification

  • pixi run lint clean.
  • 749 passed / 10 skipped across mote_fleet/test, mote_bringup/test and
    mote_tasks/test. The skips are the JSON Schema conformance set —
    jsonschema is not installed in this env, so they skip here as in CI; the
    binding schema's enum was read directly and does contain external.
  • 97 node UI tests, including new ones asserting that pose/vertex/body edits and
    freshZone all carry the editor anchor while an untouched zone keeps its own.
  • Driven in a real browser against pixi run fleet-ui-check: editing pickup's
    pose and saving stored method: external, by: fleet-ui-check (zone-editor)
    with a server timestamp, while untouched dropoff and home kept taught.
    The review pane showed the reworded validator warning ("no geometry for any of
    this floor's places, so every name resolves unbound…") and the new
    inherited-zones tooltip.
  • The goto refusal and site info output were read back directly:
    zone 'store room' is a place on this floor, but the map revision this robot is running has no geometry for it — place it in the dashboard's zone editor and promote, pull the revision that binds it, or drive there and run save-zone, with the galley still distinct as unknown_name.
  • Acceptance grep returns nothing that survives review: the remaining hits name
    save-zone as one of three remedies, or are unrelated (arm poses, the fetch
    tree, a mapsync config comment).

Branch zones-unbound-remedies, one commit, not pushed.

…anchor provenance

The zone layer was written when a zone got geometry one way: a robot was
driven there and `save-zone` captured the pose. `segment-map` and the
dashboard's zone editor have since added two more, and M4 made a binding
travel inside a map revision, so a robot can hold geometry for a floor it has
never entered. Five behaviours and about sixty prose sites still said
otherwise.

**`unbound` is a fact about the revision, not about where the robot has been.**
The goto/fetch refusal, `task_server`'s boot warning, `site info` and
`bundle.validate`'s warning each said the remedy was to drive there. They now
name the mechanism — no geometry for this name in the binding this robot holds
— and all three remedies: place it in the dashboard's zone editor and promote,
pull the revision that binds it, or drive there and `save-zone`, which is the
one that also measures an approach heading. `site info` puts the remedy on its
own line under the count, since a tally and three sentences do not belong in
one parenthesis.

**Geometry an operator places is no longer recorded as one a robot drove to.**
`zone.anchor()` defaulted to `taught` and the editor sent no anchor at all, so
every click, drag and reshape was stored as a measurement — in the one field
that exists to tell a later reader whether to trust a coordinate after the map
changes. The editor now stamps zone/v0's `external` (`EDITOR_ANCHOR`, through
`reanchored`) on every zone whose geometry it moves, and leaves an untouched
one carrying what it arrived with, so a `segment-map` room stays `derived`
until somebody reshapes it. `external` is zone/v0's closest fit rather than an
exact one — the spec glosses it as an off-platform localisation system — but
the enum is closed, and the alternatives were recording a click as a
measurement or as an algorithm's output. A successor revision should carry a
method for it (#616).

The browser says only the method and `by: zone-editor`; `_stamp_anchor` fills
in `at` from the server's clock and rewrites `by` to name the operator holding
the token, which is the half a browser cannot be trusted for. Carrying it
through took two fixes: `bundle.parse_zones` dropped `anchor` outright, and
`zone.split` stamped `taught` over whatever arrived — between them the editor's
own output was relabelled on the way back in. `read_anchor` validates one at
the parse, so a method outside the four is a 422 rather than a stored claim
nothing afterwards can distinguish from a real one.

The snap rule loses its `taught` justification with it: nothing re-snaps a
coordinate the operator did not touch, whoever put it there, and a dragged pose
does snap because a drag is a fresh coordinate with the map's precision and no
more.

The prose sweep replaces "taught" used as a synonym for "bound" throughout, and
keeps it where it means the anchor method or a floor written before a
migration.

Verified: `pixi run lint` clean; 749 passed / 10 skipped across
`mote_fleet/test`, `mote_bringup/test` and `mote_tasks/test` (the skips are the
JSON Schema conformance set — `jsonschema` is not in the env); 97 node UI
tests. Driven in a real browser against `fleet-ui-check`: editing `pickup`'s
pose and saving stored `method: external, by: fleet-ui-check (zone-editor)`
with a server timestamp, while untouched `dropoff` and `home` kept `taught`;
the review pane showed the reworded validator warning and the new inherited-
zones tooltip. The goto refusal and `site info` output were read back directly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUbZKWZ1coJyJAiaqdVvaS
@MJohnson459
MJohnson459 merged commit 5eb30e4 into main Sep 2, 2026
6 checks passed
@MJohnson459
MJohnson459 deleted the zones-unbound-remedies branch September 2, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant