Bookkeeping, a diagram that refused to lie, and the plan for Phase 5
Four smaller jobs: the ROS graph documentation, the GNSS static test for gate 1, build records in Elasticsearch, and a written plan for Phase 5 (perception), which is now up for review before any of it is built.
The diagram that refused to lie
The software architecture diagram is generated, and every arrow it draws is checked against a capture of the live ROS graph. The capture was still taken from the old square-drive experiment, from Phase 2. Re-capturing it from a real mowing mission (18 nodes, 41 topics) made the diagram generator refuse to run:
AssertionError: square_driver doesn't subscribe to /odom in the live graph
That's the check doing its job: the diagram showed a robot that no longer exists, and nobody noticed for a day.
It now shows the real chain: the mission sends *_raw commands, and every one of them passes safety_gate
(lidar field, STOP latch, watchdog) before it reaches the robot.
GNSS static test (gate 1)
The first acceptance test on real hardware will be the push-cart's GNSS on a tripod for 10 minutes. The script that
will judge it now exists, and like the end-of-line test it reads only /gps/fix, so it judges a simulator bag and a
real ZED-F9P bag the same way. "RTK fixed" means the fix status and a reported σ ≤ 5 cm, because u-blox drivers
report RTK float with the same status, and only the covariance tells them apart.
| gnss-static-1 (sim, no fix for the first 45 s) | Expected | Measured |
|---|---|---|
| Time to RTK fixed | ≤ 300 s | 44.8 s |
| Fixed after the first fix | ≥ 95% | 100% |
| Scatter, 95% per axis | ≤ ±3.0 cm | ±2.9 cm |
It passed by 1 mm, and that's not luck. The simulated receiver's noise is σ = 1.5 cm per axis, and 95% of a Gaussian lies within 1.96σ = 2.94 cm. Our sim is set right at the gate's limit. A real RTK receiver in the open should do better; if it doesn't, that's worth knowing before it's bolted into a mower.

Build records, as data
Every robot gets a JSON build record in build-records/<serial>.json, and make builds validates them, indexes them
into Elasticsearch (one document per robot, one per part), and prints what each robot is allowed to do:
SIM-0001: ok; end-of-line PASS; gates none; may drive blade-off: True; may mow supervised: False
The rule that matters: only results on the robot's current revisions count. Flash new software and the old end-of-line PASS and gate results become history; the permissions disappear until the tests are rerun. A unit test pins exactly that.
The Phase 5 plan, for review
Phase 5 is "don't run over the dog". The plan is in docs/phase5-perception.md, and its first line is the most
important decision: perception can only add caution, never remove it. A neural network may make the robot stop,
slow down or keep further away, but it can never tell the lidar "that's only grass, carry on". That keeps an
untestable component out of the safety chain, and it has an honest cost: a hedgehog lower than the grass is then
caught only by the camera and by a rule that's cheap and certain, no mowing between dusk and dawn, when hedgehogs
are out.
The order puts the deterministic work first: real grass in the simulator (the flat lawn has been flattering the lidar), then moving people and dogs (anything that moves is treated as a person, no camera needed), and only then the camera, the detector and the real-image dataset that's the only honest measure of its accuracy. Seven decisions in it are Chris's to make, starting with that first one.