Verification
Status: evidence ledger. Each section records what ran, at which tier, the observed result and its boundary. Tiers: Q0 static/unit (seconds), Q1 CAD verification (minutes), Q2 simulation and integration smokes, Q3 hardware. Nothing here has been exercised on a physical robot (no Q3 yet).
2026-11-01 — print kit after the builder's second review
| Check | Command | Tier | Observed result | Boundary |
|---|---|---|---|---|
| Hole-step detection | strido_publish.print_bridges.hole_steps on every kit part as it lies | Q0 | 4 steps found and cut: one bearing seat each in inner_tibia and outer_tibia, two 625ZZ seats (16.3 → 12.3 mm) in outer_shoulder_frame (the parts the builder named); steps under one extrusion line (0.4 mm) and part rims are skipped; after the cut the detector finds none. Section images at the step show wide hole, slot, square, narrow hole. Review sliced the cut parts: outer_shoulder_frame prints slot, square and hole at 4.2/4.4/4.6 mm with no support; in both tibias the seat sits under a boss 7.4 mm above the bed and PrusaSlicer still fills the bore with support (the notes now say to block it); cut heights are snapped to the slicing layers | geometric detection; a seat that is not round is not handled; slicer support placement checked by hand on these three parts |
| Printability with the new orientation and cuts | make printability (PrusaSlicer 2.9.6) | Q1 | 34 parts and 13 plates: 0 fail, 4 warn (thin skins in the upstream servo mounts, 0.38–0.53 mm); kit 958 g, 100 h; servo mounts 19 mm tall like a U, 2 % overhang; inner_tibia on its flat side (1638 mm² on the bed, was 71 mm²) | slicer estimate; no part printed yet |
| Strength and print-kit tests | python -m strido_verify.strength, pytest tests/verify/test_print_kit.py tests/verify/test_strength.py | Q1 | 19 checks ok (sections read from the cut STLs); 6 tests passed | — |
2026-11-01 — stiff long link and print kit orientation
| Check | Command | Tier | Observed result | Boundary |
|---|---|---|---|---|
| Strength with the stiff long link | python -m strido_verify.strength | Q1 | 19 checks, 19 ok, 0 warn, 0 fail; long link buckling S = 2.53 (upstream link 0.34, ADR-010), tension/compression S = 4.34 including the bending from the 2 mm shaft offset (secant formula; review finding, the first run checked F/A only and gave 10.3), eye bearing pressure S = 7.1; lowest factor now the tibia halves (2.22); no accepted deviation left (accepted: {}); mass 3.07 kg (+24 g for the four links), landing foot force 45.2 N | walls-only sections, PETG data sheet values |
| Gate seen red while designing the link | python -m strido_verify.rom, neighbour-distance sweep (throwaway script, 4 thigh × 43 calf poses per leg) | Q1 | a link centred on the pin line came within 0.25 mm of the femur at the stretched knee and touched the short link's washer at full flex; the ROM sweep moved the calf limit from −45° to −50° (seen as a hardware/joint_limits.yaml diff). Shaft 2 mm off the pin line with the upstream eyes: every neighbour ≥ 1.0 mm (femur 1.1, short link 1.4), joint limits unchanged | point-sampled distances; the gates below are the evidence |
| Full CAD verification | make verify | Q1 | interference 0 unexplained overlaps (4 poses, 483 solids); clearance 0 fail, 4 tight (unchanged); ROM limits unchanged (only the collision evidence beyond the limits lists the new link); fits 0 fail (sleeve seats 4.85–4.95 mm in the 5.0 mm bores); wiring 174 checks, 0 fail, 1 warn; geometry bindings in sync | nominal geometry |
| Print kit orientation | make print, make printability (PrusaSlicer 2.9.6) | Q1 | femur, top bracket, femur servo connector, side beam and right shoulder re-oriented; camera base on its base; stiff long link flat, no supports; the servo mounts stay upright (review: on their side the hip-horn pilots would print horizontal, out of reach of the ADR-009 hole compensation). 34 parts and 13 plates: 0 fail, 4 warn (thin skins in the upstream servo mounts); kit 1002 g, 103 h (was 1021 g, 104 h). Triggered by a builder's review: the plates kept the published STL poses (top bracket 56 mm tall on a 19 × 6 mm footprint, femur on 2 mm² of bed contact) | slicer estimate; no part printed yet |
| Strength and translation tests | pytest tests/verify/test_strength.py, make drift | Q0 | strength tests 4 passed (acceptance mechanism tested with an injected ADR entry; the stiff link's walls-only I matches the hollow-square formula within 2 %) | — |
2026-10-31 — German site, AI assistant, MCP server and Agent Skill
| Check | Command | Tier | Observed result | Boundary |
|---|---|---|---|---|
| New: translation catalogues complete and structurally sound | node docs-site/scripts/i18n-catalogues.mjs check (in make drift) | Q0 | 5746 of 5746 German segments; 0 broken (code spans, math, link targets, HTML tags, bold markers, table cells, line count) | structure only: wording reviewed by spot checks and a terminology pass, not by a native proofreader |
| Gate seen red | same | Q0 | failed with 5431/5433 while two new paragraphs were untranslated, and during translation on bold-marker mismatches; a number re-grouped by the translator (1126 → 1.126) now makes the segment untranslated instead of keeping a stale figure (test fails on the earlier rule) | — |
| Translation engine tests | node --test 'docs-site/scripts/*.test.mjs' (in make test-web) | Q0 | 7 passed: every untranslated page renders back to its English source, German headings keep the English ids (repeats numbered like VitePress), numbers carry over in German notation, merge keeps/opens/drops segments, structure check | synthetic segments plus every published page |
| German site build | make docs-build, built HTML compared | Q0 | builds with the dead-link check; 60 pages × heading ids identical in /docs/** and /de/docs/** (0 mismatches); every German page 100 % translated | — |
| German site in the browser | managed Chrome, /de/, /de/docs/shop | Q2 | German navigation, home, shop (plan A € 978,76), sidebar, language switch | visual spot check |
| New: assistant service tests | cd assistant && npm test (in make test-web) | Q0 | 34 passed: owner scoping, header-secret and cxu_ token checks (refusals cached), SSE event order, stop stores stopped, regenerate without a duplicate question, last tool round with tool_choice: none, attachment magic bytes and limits, per-user quota, body limits, MCP initialize/list/call | fake model transport, fixture knowledge |
| Assistant against the real model | make assistant + make docs, managed Chrome | Q2 | Keyring claude-sonnet-5: "front knee loop" answered 32 mm = 1.2 × 25.6 mm with KaTeX and four source links; pinned sidebar persists across pages; a pasted 2400 × 1600 image of Step 1 identified in German on /de/docs/assembly/ with a link to Step 1; Stop then Retry stored one question and one answer; conversations listed, renamed, deleted over the API | local development identity; Keyring credential state on the stack not exercised |
| MCP with a real client | @modelcontextprotocol/sdk Streamable HTTP client against /mcp | Q2 | tools search_docs, read_page, list_pages, search_parts; German search for "loop key arming" returns the glossary entry with its /de/ link; read_page returns the wiring-list section | local, no app token (dev identity) |
| Production images and identity | docker compose -f docker-compose.docs.yml -f docker-compose.cortex.yml (both targets) | Q2 | read-only containers start; assistant without the header secret 401, with it serves /me (disabled without Keyring) and writes to its volume; a fake cxu_ token 401; docs 200 on /de/docs/ai | Cortex gateway route, Keyring and app key exercised only after deploy |
| Deployed 0.3.1 (two services, own gateway route) | cortex deploy (job mvwtwfpf-530e539a), cortex apps show strido, curl | Q2 | healthy on c80f807 ("SSO route HTTP 302, upstream HTTP 200; 2 live containers"); /api/health open (61 en / 61 de pages, model configured); /, /de/, /llms.txt, /api/assistant/me, /mcp without a session redirect to the SSO login; a fake cxu_ bearer reaches the assistant and gets 401 from Cortex's token check | signed-in browser pages not re-checked (the relay tab's session had expired) |
| MCP on the deployed site with a real app token | token minted with cortex api post /api/v1/apps/strido/tokens (1 day), SDK client, then revoked | Q2 | tools/list 4 tools; German search_docs "Hauptsicherung Bemessung" answers from the German glossary; search_parts "servo" returns the BOM line; /api/agent/llms.txt 200, skill ZIP 615 655 bytes; the token gets 403 on /api/assistant/*. cortex apps connect strido answers 404: Control records .cortex/package.json only for Cortex-created apps (TODO) | one token, one client |
2026-10-31 — wiring harness routed and checked in the CAD
| Check | Command | Tier | Observed result | Boundary |
|---|---|---|---|---|
| New: wiring gate | python -m strido_verify.wiring (in make verify) | Q1 | 37 cables in 71 runs, 174 checks: 0 fail, 1 warn (W23, the LiDAR's own cable: length not published). Reach (cut branches counted with their slack and crimp): tightest the front knee leads (243/241 of 250 mm usable from 260 mm), W19 STEMMA QT (125 of 140 mm). Stock: 18 AWG fits only with the BOM raised to 2 m per colour. Hip loops: front 30/32 mm vs. 28.9/30.7 mm needed (1.2 × tie-to-tie 24.1/25.6 mm), rear 22 mm vs. 19.4 mm. Clearance in 13 poses: no conductor through a part; nearest moving part ≥ 1 mm | nominal geometry; bend radii and loop shape not modelled; lead exits and supplied lead lengths partly assumed (TODO) |
| Gate seen red during design | same | Q1 | first runs failed: W00/W15/W17/W19/W20 and three front servo leads too short, 18 AWG and STEMMA QT stock short, the front bus jumper 0.3 mm into the FL calf servo at the inward hip limit, the pack lead's mated plug inside the front UBEC, a conductor drawn outside its routed envelope (the gate now checks every drawn conductor). After the review's reach fix the front knee leads (258/259 mm) and W22 (196 mm) failed: knee crossing moved to the bus holder and the front opening tie onto the cables' natural path, W22 now cut 22 AWG wire. Each fixed in the data or the CAD, not by a looser limit | — |
| Review findings fixed | reviewer subagent on the change, then make verify | Q1 | reach ignored branch slack and counted the PCA end as a plug (both fixed; regression test fails on the old rule); tie mounts were not in the obstacle grid (now catalogue parts); W10/W11 were drawn as pairs onto a two-terminal INA260 (now single wires + W24 ground); W01 unwrapped although unfused (spiral wrap per cable); W04 trunk missing from the main fuse's list; manual dressed the harness before the rear-panel wiring existed and plugged the Pi before a supply check (Step 20 now does both, in that order) | — |
| Wiring gate unit tests | pytest tests/verify/test_wiring.py tests/verify/test_electronics.py | Q0 | 17 passed: reach margin, a cut branch needing its slack, longest servo-lead branch, a port used twice and a pair taking both rails, a loop 1 mm shorter than needed, a 22 AWG servo branch behind the 25 A fuse, a branch held to its own fuse | synthetic rows plus the tracked YAML |
| Full CAD verification with the cables and the re-placed power parts | make verify | Q1 | pass: interference 0 unexplained overlaps (first run caught the rotated LiDAR adapter in a pedestal screw column, fixed), clearance unchanged (0 fail, 4 tight), sensors: cables block no LiDAR ray and stay out of the camera view, wiring 173 ok / 1 warn | cables excluded from the part-to-part gates (they have their own); tie mounts are checked |
| Electronics gate on the routed cables | python -m strido_verify.electronics | Q0 | 81 checks pass (3 without margin); the old 18 AWG tap behind the 25 A fuse would have failed and is gone (logic holder's own 14 AWG lead) | ampacity table, not thermal model |
| Published artefacts | make bom models manual renders viewer catalog | Q1 | plans A €978.76 / B €1,126.97 / C €1,145.24 / D €987.73 (servo extensions out, spiral wrap, tie mounts, crimp contacts, solder, 2nd STEMMA QT and 2nd 18 AWG set in); CAD mass 3.008 → 3.050 kg (cables weighed per instance, lump removed); manual 22 steps (wiring Steps 14–16 and 20), new wiring.png render, GLB with the harness | prices observed 2026-10-31 |
2026-10-31 — documentation site: shop, print kit, product pages, Learn wiki
| Check | Command | Tier | Observed result | Boundary |
|---|---|---|---|---|
| Shop pricing reproduces the BOM optimiser | managed Chrome on the built site: complete robot in the cart, plans A–D | Q2 | cart totals €963.67 / €1,107.16 / €1,128.66 / €972.64 with 8 / 4 / 9 / 8 shops, equal to make bom; kit card shows €963.67 incl. shipping (€924.14 parts + €39.53); reviewer recomputed the same totals from shop.json | offers observed 2026-10-30; nothing ordered |
| Print package download | print kit page, complete robot, ZIP captured from the page | Q2 | strido-print-package-1-robot.zip, 49 files (README, profile, 34 STLs named with quantity, 13 plates), unzip -t clean; parts sheet 67 pieces, 719 g graphite, 301 g lime, 104 h 16 min | ZIP built in the browser, stored (uncompressed) |
| Product information complete | make bom, pytest tests/workspace/test_products.py | Q0 | 59 BOM lines and 34 printed parts covered, every Learn link resolves; the test also proves a removed part and a dead wiki link are reported | wording reviewed, datasheet values partly from memory (flagged in the review) |
| Site build and links | make docs-build | Q0 | builds; dead-link check still fails on a missing public file (/viewer/missing.glb) and on a directory link (/viewer/), and accepts existing STL/GLB/INI/log files | only internal links are checked |
| Site audit | managed Chrome, all sidebar pages at 1440×900 and 390×844, light and dark | Q2 | 0 broken images, 0 failing internal links, 0 console errors, no horizontal overflow on phones; 35 findings (3 broken, 18 confusing) fixed, re-checked on the rebuilt site | visual review, no automated accessibility suite |
| Thread-locker fallback corrected | python -m strido_verify.fits | Q1 | 223 screws ok 86 / warn 137 / fail 0, seats 32 / fail 0 (unchanged); weak-pilot and ADR-009 actions now name a CA-hardened pilot | wording change only |
| New: strength of legs and joints | python -m strido_verify.strength | Q1 | 19 checks: 18 ok, 1 warn, 0 fail. Long link buckling in the leg plane S = 0.34 at the worst pose with the knee servo at stall (known weak point, ADR-010, bounded by min_safety 0.3); femur bending S = 3.2, tibia S = 2.2, hip horn hole pressure S = 2.6; pilot torque limits 0.05–0.35 N·m; reviewer recomputed femur bending, buckling and a pilot torque by hand and found the link axes swapped in the first version (fixed: pin axis from the bores); section math unit-tested against b·h³/12 (mutation that keeps hole orientation fails: area 104 instead of 56 mm²) | static, walls-only sections, servo stall torque as design load (back-driving takes more), assumed 3 × weight landing, K = 0.7 across the plane; no fatigue/creep; not a physical test |
| Datasheet check of disagreements and product specs | research agent against manufacturer sources (Hobbywing, Gens ace, Raspberry Pi, Seeed, NXP, ST, TI, Slamtec, SKF, Koyo, Prusament, Bossard) | static | UBEC 15 A peak confirmed (20 A was the aircraft model); LiPo 60C; all listed product specs ok; filament offers were PLA (now PETG); cooler "JST" unverified (removed) | bearing ratings of branded parts used as upper bounds for unbranded ones |
| Amazon one-click cart | signed-in Chrome, gp/aws/cart/add.html with one ASIN (no click on the page) | Q2 | redirects via associates/addtocart to an empty cart: the form needs an Associates tag; replaced by the guided checkout (window.open into one tab per shop, checklist persisted) | Amazon policy can change |
| Plans after the PETG offers | make bom | Q0 | A €965.27 (8 shops), B €1,107.41 (4), C €1,130.26 (9), D €974.24 (8) | prices observed 2026-10-30/31 |
| LaTeX rendering of the wiki and strength report | make docs-build, built HTML scanned, managed Chrome at 1440 px and 390 px | Q0/Q2 | 0 MathJax error nodes, 0 unrendered $…$ pairs in the built pages (15 one-line <details> answers had to be split to render); theory pages 73–121 equations each; no horizontal page overflow at 390 px (display equations scroll inside the column) | visual spot checks of 4 pages, not every equation |
2026-10-31 — v0.2.0 rework (harness, packages, new gates, fixes)
| Check | Command | Tier | Observed result | Boundary |
|---|---|---|---|---|
| Lint, strict types (core, NT-X, workspace), import layers | make lint typecheck layers | Q0 | ruff clean; mypy: no issues in 21 files; 3 import-linter contracts kept | CAD/verify/publish packages are linted, not strictly typed |
| Generated sources in sync | make drift | Q0 | geometry bindings, electronics report and NT-X assets up to date; seen red when geometry.py was reformatted and when limits changed | text assets compared byte for byte; NT-X meshes only for presence |
| Python tests with coverage gate (80 %) | make test | Q0 | 73 passed, coverage 83.7 % | modules that run in NT-X / over stdio are excluded from coverage and exercised by the smokes |
Electronics: firmware header vs hardware/electronics.yaml in both directions, unique GPIO/I²C, servo channels, loads ≤ fuses ≤ wire ampacity | python -m strido_verify.electronics | Q0 | 44/44 checks pass; tests prove a swapped pin, a header pin missing from the YAML, duplicate channel, address clash, undersized wire and UBEC overload all fail | currents are datasheet/[ESTIMATE] values, not measured |
| Firmware core (kinematics with new limits, protocol, safety) | make test-firmware | Q0 | 15/15 passed | native host build, not the ESP32 |
| Interference, 479 solids, 4 poses | make verify | Q1 | 0 unexplained overlaps (292 classified overlaps at stand) | mesh geometry, nominal sizes |
| New: fits and tolerances of every screw (223) and bearing/bushing seat (32) | python -m strido_verify.fits | Q1 | 0 fail, 137 with a builder action (drill a tight hole on a nut joint, CA-hardened pilot / hole compensation on a weak pilot), 86 ok; 29 of the warnings are the accepted upstream servo-mount pilots (ADR-009: fail without the acceptance). Seen red with 168 → 0 failures while fixing the model and three real defects (below); engagement counts only material the screw cuts; a bearing without a seat fails | CAD sizes with a 0.15 mm FDM shrink model; real prints vary by printer |
| New: moving-part clearance, 13 operating poses | python -m strido_verify.clearance | Q1 | 0 fail (< 0.3 mm), 4 tight (0.73–0.79 mm at the hip-out limit), 80 rotational interfaces, 72 joint interfaces (only across real joints/linkage pins); seen red: both front feet collided in hips_in (50 fails) before the limit fix; restricting the exemption exposed the RR shoulder / back shell pair (0.73 mm, previously exempt because it touched at the CAD pose) | nominal geometry; servo backlash not modelled |
| Range of motion with mirrored partner leg and 0.3 mm clearance | python -m strido_verify.rom | Q1 | hip limits now FL/RL −17.5…50/45°, FR/RR −50/−45…17.5° (inward travel reduced from 25–30° so the feet cannot meet) | grid 5°/2.5° |
| Sensor field of view | python -m strido_verify.sensor_fov | Q1 | 0 of 720 LiDAR rays blocked, camera sees 0 % robot, 5 poses | — |
| New: print kit complete and printable | pytest tests/verify/test_print_kit.py | Q1 | 34 part types / 67 pieces, every STL a closed manifold within 220 × 220 × 250 mm, every piece on exactly one 3MF plate; seen red: the camera base and tilt plate were missing from the plates (trimesh drops 3MF nodes named camera*) | slicer settings not simulated |
| BOM covers every CAD part and fastener | make bom | Q1 | all strategies covered; lowest total €963.67 (8 shops) | prices observed 2026-10-30 |
| MuJoCo gait walk | make sim-walk | Q2 | 0.15 m/s for 10 s → 1.104 m, height 0.174 m, yaw −1.7° | simulator heading hold |
| Gazebo Harmonic headless walk, gait node (ROS 2 Jazzy, Docker, arm64) | make sim-gazebo | Q2 | 8 s at 0.15 m/s → 0.98 m forward, lateral −0.003 m, height 0.178 m, roll/pitch < 0.01 rad; joint_states, IMU, scan (min 0.49 m), camera and odom all publishing | simulator only; same container as the robot image minus the Pi camera stack |
| Released PPO policy with the new limits | make rl-eval | Q2 | 0.2 m/s command → 0.215 m/s mean, 2.15 m in 10 s, upright | MuJoCo only |
| PPO pipeline | make rl-smoke | Q2 | 20 k steps in 6 s, policy exported and evaluated (not trained to walk) | pipeline check only |
| New: hardware MCP server over stdio | make mcp-smoke | Q2 | 10 tools listed, 9/9 calls ok | measure_gap covered by the CAD tests, not the smoke |
| New: NT-X integration inside the NT-X harness environment | make ntx-smoke | Q2 | catalog robot (12 joints, 3.008 kg) and skill load; stands 2 s in NT-X MuJoCo (base 0.178 m, tilt 0.0°); adapter strido-serial registered; RobotPort ↔ protocol-level fake MCU: the real pd-pose runtime output (gains + gravity feed-forward up to 0.06 N·m) → position target → state with the echoed command sequence, IMU loss raises and latches the protective stop, protective stop latches, close frees the device; seen red when the model path was wrong | fake MCU speaks the real protocol but is not the firmware; hardware sessions also need a base estimate (NT-X odometry gate) |
| New: CAD regression tests for the fixed fit defects | make test-cad | Q1 | 3 passed (no screw fit fails; all 4 hip-pivot screws thread into a servo mount incl. the _fl/_fr variants; LiDAR pedestal screws engage the pedestal); seen red with the old 6 mm pedestal pilot depth (2 failed: solid/fail) | same CAD-size model as the fits gate |
| New: docs site and 3D viewer | make docs-build, preview in Chrome | Q2 | site builds; viewer loads the GLB, explode / step / shells / fasteners / picking work (picked part shows name, id, count, step); parts catalogue shows 76 part types; seen broken: every unique mesh was also exported at the GLB origin in millimetres (a 100 m "wedge" around the robot) until the geometry was stored without its own node | desktop Chrome only; no mobile/Safari check |
| Harness gates (agent config drift, env docs, arch scan, secrets, npm audit) | make harness-check | Q0 | pass; gitleaks no leaks; npm audit 0 vulnerabilities; three v0.1 CAD definition modules baselined in the shrink-only arch-scan ledger | Semgrep/Trivy skipped (heavy security off) |
| New: printability: every part sliced by a real slicer | make printability (PrusaSlicer 2.9.6, hardware/print/prusaslicer.ini) | Q1 | 34 part types and 13 plates: 0 fail. Each part sliced as prescribed (supports/brim) with no error message and complete G-code; supported parts also sliced without supports and show only issues supports fix; every plate prints all its parts without tool-path conflicts. Kit 1021 g PETG (719 g graphite, 301 g lime: one 1 kg spool each), 104 h. Wall-thickness scan 0 fail, 5 warn (upstream femur and servo mounts: walls 0.65–0.8 mm, printable but thin). Seen red: electronics deck without supports (floating bridge anchors under the IMU pocket); tibias/short links/hip pins in published orientation (low bed adhesion, found once supported parts got the extra unsupported slice); plates with 4 mm part gaps (tool-path conflicts); deck with supports removed from the print list (unit test of the parser and a gate run) | slicer simulation, not a test print; thin regions shorter than 5 mm and narrower than 1 mm count as cusps |
| New: electronics voltage domains | python -m strido_verify.electronics | Q0 | 16 supply/signal checks pass: no rail can exceed a rated maximum, every nominal level is in range; 3 pass without margin because the rail minimum reaches below the rating (servo PWM and LCD at 3.3 V, the lowest level they accept); seen red: servo rail at the UBEC's 7.4 V setting and a 6.3 V UBEC (tests) | ratings from vendor pages/datasheets; rail tolerances partly [ESTIMATE]; no custom PCB exists, so no ERC/DRC/SPICE applies |
| New: documentation site on Cortex | docker compose -f docker-compose.docs.yml up --build docs; cortex deploy --ref b0fe000 (job mvvsdmmc-108830e0) | Q2 | local container: /healthz ok, /docs/downloads 200, build kit 17.5 MB application/zip (57 files, unzip -t clean), PDF 13.9 MB; deployed 0.2.1: cortex apps show strido healthy ("SSO route HTTP 302, upstream HTTP 200"); unauthenticated requests to /, /docs/downloads, the ZIP, the PDF and /healthz all redirect (302) to the Cortex SSO login; signed in (Chrome): home page renders, ZIP/PDF/GLB downloads answer 200 with the right type | one browser session; no load test |
Defects found and fixed (gates and review)
| Defect | Found by | Fix |
|---|---|---|
| LiDAR pedestal pilots 9 mm deep for screws that protrude 10 mm: the screws bottom out before clamping the shell | fits (solid at the tip) | pilot depth 11 mm (PEDESTAL_PILOT_DEPTH) |
| Front servo mounts (same STL as the rear) have no hip-pivot pilot on the face the front legs use: 5.5 mm of solid plastic | fits (solid) | Strido variants left_servo_mount_fl, right_servo_mount_fr with a Ø2.5 mm pilot (ADR-004) |
| Right-leg femur horn screws 3.7 mm off the printed femur's holes (STEP mirrors the right femurs; one STL prints all four) | fits (solid) | hardware/assembly/corrections.yaml: disc horn −30°, femur connector +22° on FR/RR |
| Left-leg thigh horn centre screws pointed away from the servo (head placed in the spline cavity) | fits (nothing holds the screw) | head pocket = longest end-opening bore in strido_cad.fasteners |
| Both front (and both rear) feet could meet under the body at the inward hip limits | clearance (hips_in) | ROM sweep with mirrored partner leg and 0.3 mm clearance (ADR-005) |
| Camera base and tilt plate missing from the 3MF plates of v0.1.0 | print-kit test | plate node names prefixed part_ |
| Right-leg hip disc horns moved by the femur correction (wrong body) | ROM (RR outward limit collapsed to 20°) | corrections apply per rigid body |
The NT-X serial port rejected every command of the shipped pd-pose skill (gravity feed-forward is never zero) and reported a level, still robot when the IMU failed | review; make ntx-smoke now runs the real runtime output | feed-forward dropped, kp <= 0 rejected, IMU loss fails closed, stale frames drained |
| CAD cache served meshes of an older source after a revert (manifest per digest, meshes at fixed paths) | make check failed after a deliberate gate break was reverted | one manifest, valid only for the digest of the last build; seen red/green by breaking and restoring PEDESTAL_PILOT_DEPTH |
| Clearance exemption covered any pair touching at the CAD pose, not only joints | review | exemption limited to bodies joined by a joint or linkage pin; exposed one tight pair (0.73 mm) |
| Electronics deck printed without supports: the IMU pocket floor is an 18 mm bridge with floating anchors | printability (slicer) | deck declared supports=True |
| Tibias (135 mm upright), short links and hip bearing pins have too little bed contact in the published orientation | printability (unsupported analysis slice) | re-oriented flat in the print kit; tibias 1.5 h instead of 2.5 h |
| 3MF plates packed with 4 mm gaps: neighbouring supported parts produced conflicting tool paths | printability (slicer) | 10 mm gaps, snug supports, first-fit packing (13 plates) |
| XT60 window corners left 0.26 mm slivers in the bezel boss; FDM-rounded inner corners would bind the square XT60 hood | thickness scan, review | boss reduced to an upper and lower arc; 0.5 mm dog-bone corner reliefs in bezel and clamp ring |
| A 6.2 mm screwdriver bore in the 6 mm bus-holder divider left 0.1 mm slivers | thickness scan | access slot instead of the bore |
| UBEC acceptance window (6.0 ± 0.1 V) allowed 6.1 V on a servo rated to 6.0 V | voltage-domain check | accept 5.8–6.0 V unloaded |
2026-10-30 — v0.1.0
Excerpts of the command output are linked per row (docs/verification/logs/); deliberate gate breaks are in gates.txt. Commands refer to the v0.1.0 layout (tools/…).
| Check | Command | Tier | Result | Log |
|---|---|---|---|---|
| Fasteners derived from the measured hole stacks; placed fasteners must equal the rule totals | make verify (hole_stacks.py, fasteners.py) | static | all stacks matched; placements = rule counts (gate proven red by un-merging the split tibia-pin stacks) | verify.txt |
| Interference of 479 solids (printed, purchased, fasteners) at stand 170 mm, crouch 120 mm, tall 210 mm and the CAD pose | interference_check.py | static | 0 unexplained overlaps in all 4 poses; every remaining overlap is classified (thread engagement, seating contact, joint, upstream fit, upstream fastener on its measured hole axis) – interference.md | verify.txt |
| LiDAR occlusion (720 rays) and camera self-view, 5 poses incl. trot and full hip abduction | sensor_fov.py | static | 0 rays blocked; robot in 0.0 % of the camera image – sensors.md | verify.txt |
| Range of motion: thigh × calf grid (5°) at zero abduction, transmission ≥ 25°; hip range at stand and mid pose | rom_sweep.py | static | limits as committed in hardware/joint_limits.yaml (no diff after the run) | verify.txt |
| Generated geometry in firmware/Python matches the YAML | codegen.py --check | static | no drift | verify.txt, test.txt |
| Printability: closed manifold, fits 220 × 220 × 250 mm, on the bed | make print | static | 32 part types / 67 pieces pass | docs.txt |
| BOM supplies every purchased part and fastener of the CAD for the offers each strategy buys (packs rounded up, fasteners + max(2, 10 %) spares) | make bom | static | all covered in all 4 strategies; gate proven red by removing the M3 × 10 supply | docs.txt |
| Python core: protocol (CRC vector shared with the firmware, COBS, corruption, resync), IK/FK, knee linkage inverse, gait limits incl. the knee-crank clamp, CLI calibration and the CLI's refusal when the controller keeps the servos relaxed (fake controller on a pseudo-terminal) | pytest | unit | 27 passed; the crank-limit test fails when the clamp is removed | test.txt, gates.txt |
| Firmware core: kinematics, protocol, watchdog, e-stop, debounced battery cut-off, no driving without the power monitor (bench calibration allowed), NaN rejection | pio test -e native | unit | 15/15 passed; the NaN test fails with the old clamp | test.txt, gates.txt |
| Firmware build for the XIAO ESP32-S3 | pio run -e xiao_esp32s3 | build | success, RAM 8.0 %, flash 16.0 % | firmware.txt |
mcu_bridge (ROS 2 Jazzy container) against a fake controller on a raw pseudo-terminal | throwaway script | integration | STATE → /joint_states (Go2 names) and /battery_state (7.7 V, −1.2 A); command → JOINT_COMMAND; NaN command dropped; relax → JOINT_COMMAND(relax); e-stop → ESTOP; no frame dropped | mcu_bridge_fake.txt |
| MuJoCo, production gait (heading hold uses the simulator's yaw) | make sim-walk | simulation | 0.15 m/s: 1.10 m in 10 s, height 0.174 m, yaw −1.7°; turn 0.6 rad/s: 197.9° in 6 s (−162.1° wrapped) | mujoco_walk.txt |
| Gazebo Harmonic headless walk, gait node (ROS 2 Jazzy, Docker, arm64) | make sim-gazebo | simulation | 1.00 m in 8 s at 0.15 m/s, lateral 0.12 m, roll/pitch < 0.1 rad, all sensor topics publishing | gazebo.txt |
| PPO policy, 20 M steps (1734 s, CPU), firmware slew limit modelled | make rl-train, evaluate.py | simulation | MuJoCo, 10 s per command: vx 0.2 → 0.210 m/s (yaw 2°), 0.15 → 0.152 m/s, 0 → stands, −0.1 → −0.068 m/s, vy 0.1 → 0.023 m/s (weak), wz 0.5 → 232° in 10 s (−128.4° wrapped) | rl_eval.txt |
Same policy in Gazebo through policy_node (sim-to-sim) | make sim-gazebo POLICY=build/rl/ppo-v1/policy.npz | simulation | 0.82 m in 8 s at 0.15 m/s, lateral 0.10 m, upright | gazebo.txt |
| Vendor CAD fetch | make vendor-cad | — | 8 of 9 downloads verified by SHA-256; the Littelfuse file is form-gated (manual download, envelope fallback) | gates.txt |
Not verified
- Nothing has been printed, assembled, wired or powered. Fits are checked against CAD and datasheets, not against printed parts (expect to tune hole and bearing fits to your printer).
- Servo current, runtime and thermal behaviour are estimates (electronics). The OE pull-up value assumes the PCA9685 board's pull-down is ≥ 10 kΩ (measure OE during reset as described).
- Datasheet envelopes (no vendor CAD): servos (upstream STEP model), WAGO 221-415, LiPo, UBEC, pushbutton, LiDAR USB adapter (size unverified).
- Upstream joint fasteners are placed from hole stacks with an approximate head side and nut orientation; they are part of the static interference check but not of the range-of-motion sweep.
- The knee is a revolute joint in Gazebo/MuJoCo; linkage compliance and backlash are not simulated. The learned policy has not seen real servo dynamics.
- The camera node on the Pi 5 needs the Raspberry Pi libcamera fork (not in the Docker image);
robot.launch.pywas not run on a Pi.