Repository navigation
Conversation
This was referenced Oct 2, 2026
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #326 +/- ##
==========================================
+ Coverage 88.21% 88.34% +0.13%
==========================================
Files 227 238 +11
Lines 36147 38346 +2199
Branches 2324 2658 +334
==========================================
+ Hits 31886 33877 +1991
- Misses 4257 4464 +207
- Partials 4 5 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
geonnave
force-pushed
the
site-editor-p6
branch
from
October 4, 2026 02:38
7dcf8f4 to
1aa7927
Compare
geonnave
force-pushed
the
site-editor-p6
branch
from
October 15, 2026 10:56
1aa7927 to
8ce090e
Compare
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
… page Any web page in the browser can send requests to 127.0.0.1 with a valid Host header, and behind a permissive CORS policy read the replies, so the Host check alone let it save site.toml; a server bound beyond loopback took any Host. The peer address, Origin and Sec-Fetch-Site are checked. AI-assisted: Claude Opus 5.5
…ng tables A patched file is read back and must give the model that was sent, so a layout the patcher does not follow is refused rather than written. AI-assisted: Claude Opus 5.5
…adable files AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
…e saving AI-assisted: Claude Opus 5.5
geonnave
force-pushed
the
site-editor-p6
branch
from
October 15, 2026 11:09
8ce090e to
51f06c4
Compare
Breaking: schema 3 calibration files are refused, so a calibration is collected again. swarmit's own calibration reader still expects schema 3 and needs its matching change before the dashboard reads these files. AI-assisted: Claude Opus 5.5
… block AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
…'s next round AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
…tangle Breaking, cross-repo: the messages and the config page (magic SWRP, 872 bytes) only match a swarmit bootloader and net core that store stations by mask, and swarmit's Python must accept mask messages before it relays them. Robots below device info 4 are refused with a reflash. AI-assisted: Claude Opus 5.5
…y-run AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
…s zero AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
… join AI-assisted: Claude Opus 5.5
…angles A station that never sees part of the site yields no sweep pair there, so the extent drops nothing a station could solve, whereas the box around its spins dropped areas such as a staging strip the robots never spun in. AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
A robot with no heading first gets a 40 mm probe move toward its first point, and its batch once it has a heading and the probe is done. While waypoints_awaiting_heading is set, the waypoint report and the mode are the probe's, so a client watching mode alone sees an early arrival. AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
…overage AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
… a placed one AI-assisted: Claude Opus 5.5
…body AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
… a file A placement's id comes from its stations, not its tag or anchor, so the same move saved twice, or a move into the source's own site, landed on an existing path; save_placed keeps that file and refuses the source's id. AI-assisted: Claude Opus 5.5
…ew file AI-assisted: Claude Opus 5.5
geonnave
force-pushed
the
site-editor-p6
branch
from
October 15, 2026 12:29
51f06c4 to
40c933b
Compare
This was referenced Oct 15, 2026
This branch has not been deployed
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.
Stacked on #315 (the site editor) and #343 (the top of the LH2 multi-station stack: #322, #323, #324, #325, #331, #343), joined by one merge commit at the bottom of this branch. Merge order: #315 and the LH2 stack, in either order, then this PR; #327 to #330 sit on this one. Until they land, the diff also carries their commits and shrinks to this PR's own as they merge.
A spin calibration (
calibrate-lh2 collect --spin) defines its own frame, metric through the spin radius, andsite init --from-calibrationbuilds a starter site around it. This lets the operator draw the site by hand instead and drop the calibration onto it afterwards, in the site editor.The placement (
dotbot/calibration/placement.py) is one rigid move for the whole calibration:Rigid2D(dx_mm, dy_mm, theta_deg)turns about the frame's zero, then shifts.place_calibration(calibration, site, transform)appliesT . Hto every station's homography (normalised), moves placement points with it, copies tracks (camera counts) and links unchanged, and returns a newCalibrationin the site, with the site's name and anchor (which a controller checks a calibration against) and the site's extent as its fence. Scale is never touched, so stations keep their geometry relative to one another. It refuses:Free mode is told from the stations' solver (
conics-free, orconics-jointfor multi-station files), not from the anchor: a placed calibration takes its site's anchor and must stay movable.Multi-station calibrations (schema 4, from the LH2 stack underneath) carry one validity rectangle per station. On a site with an extent, every station's rectangle becomes the site's extent, as a spin solve placed on a site already does in #331; on a site without one, a station's own rectangle moves with the calibration as the box round its turned corners (rounded outwards, so it only grows). The fence is the union of the station rectangles. The placed calibration records the source's id as
placed_from(#343), socollect --spin --appendcan name the calibration to append to. The overlay draws each station's rectangle and spin circles in its own colour, labelled "station i (channel i+1)", and the links between stations, a weak one dashed red. The placement module only reads the data model andconics.fit_tracks, and the solver files are not edited.One code path, three callers.
calibrate-lh2 reframe --shift --rotateof a spin calibration (which has no placement samples to re-solve from, so reframe failed on one) now goes throughplace_calibration, turning about the frame's zero.site init --from-calibrationplaces its calibration through it too, and a test pins that it writes the same calibration id as before.The editor gains a Calibration tool (
C). The operator types an id prefix, tag or path (a list of every calibration found is offered, and the newest is never picked for them), and the calibration is drawn over the site in its own frame. Dragging it moves it with the zero on the snap grid, a handle turns it in 90 degree steps about its centre (Alt for free), and there is no scale handle. Shift and turn are also typed numbers. Save calibration writes a new calibration under~/.dotbot/calibrations/<site>/(the file it was loaded from is never written), names the push command (dotbot swarm calibrate-lh2 push <id> --site <name>, with--site-changedwhen it came from another site), and says that robots holding the old placement draw in the wrong place until pushed. Its tag is the source's suffixed with the site, numbered if that tag is taken. A corner-collected calibration is drawn with a refusal, and Save stays disabled until Re-anchor is ticked. Save is also blocked while the site's anchor or extent have unsaved changes, since the placement reads them from the file.Routes:
GET /api/calibrations(each file's id, created time, kind and stations),GET /api/calibrations/{id}(the overlay: fence, each station's index, channel, rectangle and centres, links),POST /api/calibrations/{id}/placewith{dx_mm, dy_mm, theta_deg, reanchor}(200 with the new id, path and push command, 422 with the refusal).Plan: rendered copy https://claude.ai/artifact/LHUXjhx1nMWifC2oGRBayt, source https://github.com/DotBots/dotbot-workspace/blob/main/plans/site-editor/plan.html (both internal).
Never overwriting. A placement's id comes from its stations, not its tag or anchor, so the same move saved twice lands on the same id: the file already there is kept as it is and returned, rather than rewritten with a renumbered tag. A move that leaves the id of the source (for example
reframe --shift 0,0into the source's own site) is refused, so the source can never be written.reframeof a spin calibration andsite initsave through the same helper and number taken tags the same way as the editor. After Save the editor keeps the source drawn where it was moved to and lists the new file at once.Validation. pytest: a spin fixture placed with a 90 degree turn and an offset lands every spin centre where the move puts it within 0.1 mm, keeps distances (scale), writes a new id in the site with the original object untouched; a two-station fixture with per-station rectangles moves as one body on a site without an extent, rectangles boxed (
(0, 0, 1500, 1500)turned and shifted to(2000, 100, 3500, 1600)), fence the union; corner refusal and re-anchor; below-zero refusal naming the station and channel; tag numbering; reframe of a spin calibration writes the idplace_calibrationgives; site init's placement is the editor's. Server tests: listing, overlay by id, 404, a placement leaving the source file byte-identical, 422s, the extent warning. vitest: the rigid move and its turn about a pivot, the snapped angle, loading and saving a placement with the body sent, the corner refusal, a drag landing on the grid.npm run lint, typecheck and build;pre-commit run --all-files; full pytest 1799 passed, vitest 850 passed (after the move onto the LH2 stack: pytest 1888 passed plus one known, unrelated camera-service test failure, vitest 864 passed, typecheck clean); also the fence refused below zero after a turn, a re-anchored calibration without tracks held to zero, the same placement saved twice keeping the first file.By hand, headless, in a throwaway home:
dotbot site newa 5 x 6 m site, a one- and a two-station spin fixture plus a corner fixture under~/.dotbot/calibrations/; over REST the corner one is refused (422), a 180 degree turn is refused below zero, and a 90 degree placement writes a new file while the source's sha256 is unchanged; in headless Chrome the overlay is loaded, dragged, turned with the handle (snapped to 90 degrees, zero on the 50 mm grid) and saved, and the panel shows the push command.reframe --shiftof a spin calibration writes the placed file. That pushed robots then draw in the right place can only be proven on the bench.Docs (
doc/cli/, the LH2 guide) follow after review.dotbot/calibration/placement.pydotbot/tests/test_calibration_placement.pydotbot/console-web/src/siteEditor/Canvas.tsxdotbot/console-web/src/siteEditor/CalibrationPanel.tsxdotbot/site_placement.pydotbot/tests/test_site_placement.pydotbot/console-web/src/siteEditor/siteEditor.test.tsxdotbot/console-web/src/siteEditor/SiteEditor.tsxdotbot/console-web/src/siteEditor/rigid.tsdotbot/console-web/src/siteEditor/types.tsdotbot/console-web/src/siteEditor/api.tsdotbot/console-web/src/siteEditor/rigid.test.tsswarm_lh2.py,site_cmd.py,Inspector.tsx,dotbot/site_editor.py