Skip to content

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 ​

CheckCommandTierObserved resultBoundary
Hole-step detectionstrido_publish.print_bridges.hole_steps on every kit part as it liesQ04 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 layersgeometric 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 cutsmake printability (PrusaSlicer 2.9.6)Q134 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 testspython -m strido_verify.strength, pytest tests/verify/test_print_kit.py tests/verify/test_strength.pyQ119 checks ok (sections read from the cut STLs); 6 tests passed—
CheckCommandTierObserved resultBoundary
Strength with the stiff long linkpython -m strido_verify.strengthQ119 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 Nwalls-only sections, PETG data sheet values
Gate seen red while designing the linkpython -m strido_verify.rom, neighbour-distance sweep (throwaway script, 4 thigh × 43 calf poses per leg)Q1a 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 unchangedpoint-sampled distances; the gates below are the evidence
Full CAD verificationmake verifyQ1interference 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 syncnominal geometry
Print kit orientationmake print, make printability (PrusaSlicer 2.9.6)Q1femur, 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 testspytest tests/verify/test_strength.py, make driftQ0strength 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 ​

CheckCommandTierObserved resultBoundary
New: translation catalogues complete and structurally soundnode docs-site/scripts/i18n-catalogues.mjs check (in make drift)Q05746 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 redsameQ0failed 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 testsnode --test 'docs-site/scripts/*.test.mjs' (in make test-web)Q07 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 checksynthetic segments plus every published page
German site buildmake docs-build, built HTML comparedQ0builds 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 browsermanaged Chrome, /de/, /de/docs/shopQ2German navigation, home, shop (plan A € 978,76), sidebar, language switchvisual spot check
New: assistant service testscd assistant && npm test (in make test-web)Q034 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/callfake model transport, fixture knowledge
Assistant against the real modelmake assistant + make docs, managed ChromeQ2Keyring 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 APIlocal development identity; Keyring credential state on the stack not exercised
MCP with a real client@modelcontextprotocol/sdk Streamable HTTP client against /mcpQ2tools 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 sectionlocal, no app token (dev identity)
Production images and identitydocker compose -f docker-compose.docs.yml -f docker-compose.cortex.yml (both targets)Q2read-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/aiCortex 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, curlQ2healthy 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 checksigned-in browser pages not re-checked (the relay tab's session had expired)
MCP on the deployed site with a real app tokentoken minted with cortex api post /api/v1/apps/strido/tokens (1 day), SDK client, then revokedQ2tools/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 ​

CheckCommandTierObserved resultBoundary
New: wiring gatepython -m strido_verify.wiring (in make verify)Q137 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 mmnominal geometry; bend radii and loop shape not modelled; lead exits and supplied lead lengths partly assumed (TODO)
Gate seen red during designsameQ1first 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 fixedreviewer subagent on the change, then make verifyQ1reach 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 testspytest tests/verify/test_wiring.py tests/verify/test_electronics.pyQ017 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 fusesynthetic rows plus the tracked YAML
Full CAD verification with the cables and the re-placed power partsmake verifyQ1pass: 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 warncables excluded from the part-to-part gates (they have their own); tie mounts are checked
Electronics gate on the routed cablespython -m strido_verify.electronicsQ081 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 artefactsmake bom models manual renders viewer catalogQ1plans 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 harnessprices observed 2026-10-31

2026-10-31 — documentation site: shop, print kit, product pages, Learn wiki ​

CheckCommandTierObserved resultBoundary
Shop pricing reproduces the BOM optimisermanaged Chrome on the built site: complete robot in the cart, plans A–DQ2cart 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.jsonoffers observed 2026-10-30; nothing ordered
Print package downloadprint kit page, complete robot, ZIP captured from the pageQ2strido-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 minZIP built in the browser, stored (uncompressed)
Product information completemake bom, pytest tests/workspace/test_products.pyQ059 BOM lines and 34 printed parts covered, every Learn link resolves; the test also proves a removed part and a dead wiki link are reportedwording reviewed, datasheet values partly from memory (flagged in the review)
Site build and linksmake docs-buildQ0builds; 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 filesonly internal links are checked
Site auditmanaged Chrome, all sidebar pages at 1440×900 and 390×844, light and darkQ20 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 sitevisual review, no automated accessibility suite
Thread-locker fallback correctedpython -m strido_verify.fitsQ1223 screws ok 86 / warn 137 / fail 0, seats 32 / fail 0 (unchanged); weak-pilot and ADR-009 actions now name a CA-hardened pilotwording change only
New: strength of legs and jointspython -m strido_verify.strengthQ119 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 specsresearch agent against manufacturer sources (Hobbywing, Gens ace, Raspberry Pi, Seeed, NXP, ST, TI, Slamtec, SKF, Koyo, Prusament, Bossard)staticUBEC 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 cartsigned-in Chrome, gp/aws/cart/add.html with one ASIN (no click on the page)Q2redirects 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 offersmake bomQ0A €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 reportmake docs-build, built HTML scanned, managed Chrome at 1440 px and 390 pxQ0/Q20 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) ​

CheckCommandTierObserved resultBoundary
Lint, strict types (core, NT-X, workspace), import layersmake lint typecheck layersQ0ruff clean; mypy: no issues in 21 files; 3 import-linter contracts keptCAD/verify/publish packages are linted, not strictly typed
Generated sources in syncmake driftQ0geometry bindings, electronics report and NT-X assets up to date; seen red when geometry.py was reformatted and when limits changedtext assets compared byte for byte; NT-X meshes only for presence
Python tests with coverage gate (80 %)make testQ073 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 ampacitypython -m strido_verify.electronicsQ044/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 failcurrents are datasheet/[ESTIMATE] values, not measured
Firmware core (kinematics with new limits, protocol, safety)make test-firmwareQ015/15 passednative host build, not the ESP32
Interference, 479 solids, 4 posesmake verifyQ10 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.fitsQ10 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 failsCAD sizes with a 0.15 mm FDM shrink model; real prints vary by printer
New: moving-part clearance, 13 operating posespython -m strido_verify.clearanceQ10 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 clearancepython -m strido_verify.romQ1hip 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 viewpython -m strido_verify.sensor_fovQ10 of 720 LiDAR rays blocked, camera sees 0 % robot, 5 poses—
New: print kit complete and printablepytest tests/verify/test_print_kit.pyQ134 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 fastenermake bomQ1all strategies covered; lowest total €963.67 (8 shops)prices observed 2026-10-30
MuJoCo gait walkmake sim-walkQ20.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-gazeboQ28 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 publishingsimulator only; same container as the robot image minus the Pi camera stack
Released PPO policy with the new limitsmake rl-evalQ20.2 m/s command → 0.215 m/s mean, 2.15 m in 10 s, uprightMuJoCo only
PPO pipelinemake rl-smokeQ220 k steps in 6 s, policy exported and evaluated (not trained to walk)pipeline check only
New: hardware MCP server over stdiomake mcp-smokeQ210 tools listed, 9/9 calls okmeasure_gap covered by the CAD tests, not the smoke
New: NT-X integration inside the NT-X harness environmentmake ntx-smokeQ2catalog 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 wrongfake 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 defectsmake test-cadQ13 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 viewermake docs-build, preview in ChromeQ2site 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 nodedesktop Chrome only; no mobile/Safari check
Harness gates (agent config drift, env docs, arch scan, secrets, npm audit)make harness-checkQ0pass; gitleaks no leaks; npm audit 0 vulnerabilities; three v0.1 CAD definition modules baselined in the shrink-only arch-scan ledgerSemgrep/Trivy skipped (heavy security off)
New: printability: every part sliced by a real slicermake printability (PrusaSlicer 2.9.6, hardware/print/prusaslicer.ini)Q134 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 domainspython -m strido_verify.electronicsQ016 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 Cortexdocker compose -f docker-compose.docs.yml up --build docs; cortex deploy --ref b0fe000 (job mvvsdmmc-108830e0)Q2local 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 typeone browser session; no load test

Defects found and fixed (gates and review) ​

DefectFound byFix
LiDAR pedestal pilots 9 mm deep for screws that protrude 10 mm: the screws bottom out before clamping the shellfits (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 plasticfits (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 limitsclearance (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.0print-kit testplate 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 failedreview; make ntx-smoke now runs the real runtime outputfeed-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 revertedone 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 jointsreviewexemption 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 anchorsprintability (slicer)deck declared supports=True
Tibias (135 mm upright), short links and hip bearing pins have too little bed contact in the published orientationprintability (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 pathsprintability (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 hoodthickness scan, reviewboss 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 sliversthickness scanaccess slot instead of the bore
UBEC acceptance window (6.0 ± 0.1 V) allowed 6.1 V on a servo rated to 6.0 Vvoltage-domain checkaccept 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/…).

CheckCommandTierResultLog
Fasteners derived from the measured hole stacks; placed fasteners must equal the rule totalsmake verify (hole_stacks.py, fasteners.py)staticall 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 poseinterference_check.pystatic0 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.mdverify.txt
LiDAR occlusion (720 rays) and camera self-view, 5 poses incl. trot and full hip abductionsensor_fov.pystatic0 rays blocked; robot in 0.0 % of the camera image – sensors.mdverify.txt
Range of motion: thigh × calf grid (5°) at zero abduction, transmission ≥ 25°; hip range at stand and mid poserom_sweep.pystaticlimits as committed in hardware/joint_limits.yaml (no diff after the run)verify.txt
Generated geometry in firmware/Python matches the YAMLcodegen.py --checkstaticno driftverify.txt, test.txt
Printability: closed manifold, fits 220 × 220 × 250 mm, on the bedmake printstatic32 part types / 67 pieces passdocs.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 bomstaticall covered in all 4 strategies; gate proven red by removing the M3 × 10 supplydocs.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)pytestunit27 passed; the crank-limit test fails when the clamp is removedtest.txt, gates.txt
Firmware core: kinematics, protocol, watchdog, e-stop, debounced battery cut-off, no driving without the power monitor (bench calibration allowed), NaN rejectionpio test -e nativeunit15/15 passed; the NaN test fails with the old clamptest.txt, gates.txt
Firmware build for the XIAO ESP32-S3pio run -e xiao_esp32s3buildsuccess, RAM 8.0 %, flash 16.0 %firmware.txt
mcu_bridge (ROS 2 Jazzy container) against a fake controller on a raw pseudo-terminalthrowaway scriptintegrationSTATE → /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 droppedmcu_bridge_fake.txt
MuJoCo, production gait (heading hold uses the simulator's yaw)make sim-walksimulation0.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-gazebosimulation1.00 m in 8 s at 0.15 m/s, lateral 0.12 m, roll/pitch < 0.1 rad, all sensor topics publishinggazebo.txt
PPO policy, 20 M steps (1734 s, CPU), firmware slew limit modelledmake rl-train, evaluate.pysimulationMuJoCo, 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.npzsimulation0.82 m in 8 s at 0.15 m/s, lateral 0.10 m, uprightgazebo.txt
Vendor CAD fetchmake 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.py was not run on a Pi.

MIT licence. Mechanical design derived from Yertle by Jerome Graves. CAD, simulation and native tests are not hardware evidence — see Verification.