From d2eac926069d84d03220de7ed5d5022674bfe8a0 Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Sun, 12 Jul 2026 14:02:49 -0400 Subject: [PATCH 01/10] chore(hw): migrate KiCad files to the KiCad 10 format Via `kicad-cli sch upgrade` / `pcb upgrade` (bread-infra scripts/upgrade_kicad10.sh). Verified lossless: identical netlist, footprint positions and routing. Note `sch upgrade` does not cascade into hierarchical sub-sheets, so every .kicad_sch is upgraded individually -- upgrading only the root sheet leaves sub-sheets on the old format, which looks migrated and is not. --- hardware/thermistor.kicad_sch | 203 +++++++++++++++++++++++++--------- 1 file changed, 152 insertions(+), 51 deletions(-) diff --git a/hardware/thermistor.kicad_sch b/hardware/thermistor.kicad_sch index 59fb1b3..ff656e6 100644 --- a/hardware/thermistor.kicad_sch +++ b/hardware/thermistor.kicad_sch @@ -1,18 +1,24 @@ (kicad_sch - (version 20231120) + (version 20260306) (generator "eeschema") - (generator_version "8.0") + (generator_version "10.0") (uuid "cec2ee4f-3b2c-49e3-b320-52feec74719d") (paper "A4") (lib_symbols (symbol "Connector:Screw_Terminal_01x02" (pin_names - (offset 1.016) hide) + (offset 1.016) + (hide yes) + ) (exclude_from_sim no) (in_bom yes) (on_board yes) + (in_pos_files yes) + (duplicate_pin_numbers_are_jumpers no) (property "Reference" "J" (at 0 2.54 0) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) @@ -21,6 +27,8 @@ ) (property "Value" "Screw_Terminal_01x02" (at 0 -5.08 0) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) @@ -29,47 +37,68 @@ ) (property "Footprint" "" (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) - (property "TC_DATAsheet" "~" + (property "Datasheet" "" (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (property "Description" "Generic screw terminal, single row, 01x02, script generated (kicad-library-utils/schlib/autogen/connector/)" (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "TC_DATAsheet" "" + (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (property "ki_keywords" "screw terminal" (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (property "ki_fp_filters" "TerminalBlock*:*" (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (symbol "Screw_Terminal_01x02_1_1" @@ -84,9 +113,10 @@ (type background) ) ) - (circle - (center 0 -2.54) - (radius 0.635) + (polyline + (pts + (xy -0.5334 0.3302) (xy 0.3302 -0.508) + ) (stroke (width 0.1524) (type default) @@ -109,7 +139,7 @@ ) (polyline (pts - (xy -0.5334 0.3302) (xy 0.3302 -0.508) + (xy -0.3556 0.508) (xy 0.508 -0.3302) ) (stroke (width 0.1524) @@ -131,10 +161,9 @@ (type none) ) ) - (polyline - (pts - (xy -0.3556 0.508) (xy 0.508 -0.3302) - ) + (circle + (center 0 0) + (radius 0.635) (stroke (width 0.1524) (type default) @@ -144,7 +173,7 @@ ) ) (circle - (center 0 0) + (center 0 -2.54) (radius 0.635) (stroke (width 0.1524) @@ -191,17 +220,24 @@ ) ) ) + (embedded_fonts no) ) (symbol "Device:R_US" - (pin_numbers hide) + (pin_numbers + (hide yes) + ) (pin_names (offset 0) ) (exclude_from_sim no) (in_bom yes) (on_board yes) + (in_pos_files yes) + (duplicate_pin_numbers_are_jumpers no) (property "Reference" "R" (at 2.54 0 90) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) @@ -210,6 +246,8 @@ ) (property "Value" "R_US" (at -2.54 0 90) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) @@ -218,53 +256,74 @@ ) (property "Footprint" "" (at 1.016 -0.254 90) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) - (property "TC_DATAsheet" "~" + (property "Datasheet" "" (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (property "Description" "Resistor, US symbol" (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "TC_DATAsheet" "" + (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (property "ki_keywords" "R res resistor" (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (property "ki_fp_filters" "R_*" (at 0 0 0) + (show_name no) + (do_not_autoplace no) + (hide yes) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (symbol "R_US_0_1" (polyline (pts - (xy 0 -2.286) (xy 0 -2.54) + (xy 0 2.286) (xy 0 2.54) ) (stroke (width 0) @@ -276,7 +335,7 @@ ) (polyline (pts - (xy 0 2.286) (xy 0 2.54) + (xy 0 2.286) (xy 1.016 1.905) (xy 0 1.524) (xy -1.016 1.143) (xy 0 0.762) ) (stroke (width 0) @@ -288,7 +347,7 @@ ) (polyline (pts - (xy 0 -0.762) (xy 1.016 -1.143) (xy 0 -1.524) (xy -1.016 -1.905) (xy 0 -2.286) + (xy 0 0.762) (xy 1.016 0.381) (xy 0 0) (xy -1.016 -0.381) (xy 0 -0.762) ) (stroke (width 0) @@ -300,7 +359,7 @@ ) (polyline (pts - (xy 0 0.762) (xy 1.016 0.381) (xy 0 0) (xy -1.016 -0.381) (xy 0 -0.762) + (xy 0 -0.762) (xy 1.016 -1.143) (xy 0 -1.524) (xy -1.016 -1.905) (xy 0 -2.286) ) (stroke (width 0) @@ -312,7 +371,7 @@ ) (polyline (pts - (xy 0 2.286) (xy 1.016 1.905) (xy 0 1.524) (xy -1.016 1.143) (xy 0 0.762) + (xy 0 -2.286) (xy 0 -2.54) ) (stroke (width 0) @@ -327,7 +386,7 @@ (pin passive line (at 0 3.81 270) (length 1.27) - (name "~" + (name "" (effects (font (size 1.27 1.27) @@ -345,7 +404,7 @@ (pin passive line (at 0 -3.81 90) (length 1.27) - (name "~" + (name "" (effects (font (size 1.27 1.27) @@ -361,6 +420,7 @@ ) ) ) + (embedded_fonts no) ) ) (junction @@ -432,7 +492,6 @@ (hierarchical_label "TMSTR" (shape input) (at 111.76 87.63 180) - (fields_autoplaced yes) (effects (font (size 1.27 1.27) @@ -444,7 +503,6 @@ (hierarchical_label "5V" (shape input) (at 111.76 78.74 180) - (fields_autoplaced yes) (effects (font (size 1.27 1.27) @@ -456,7 +514,6 @@ (hierarchical_label "GND" (shape input) (at 111.76 95.25 180) - (fields_autoplaced yes) (effects (font (size 1.27 1.27) @@ -469,13 +526,17 @@ (lib_id "Connector:Screw_Terminal_01x02") (at 138.43 92.71 0) (unit 1) + (body_style 1) (exclude_from_sim no) (in_bom yes) (on_board yes) + (in_pos_files yes) (dnp no) (uuid "3c957ad9-89c7-4d88-b9c5-5192e405176f") (property "Reference" "J9" (at 140.462 92.9132 0) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) @@ -485,6 +546,8 @@ ) (property "Value" "Screw_Terminal_01x02" (at 140.462 95.2246 0) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) @@ -494,29 +557,45 @@ ) (property "Footprint" "TerminalBlock_Phoenix:TerminalBlock_Phoenix_MPT-0,5-2-2.54_1x02_P2.54mm_Horizontal" (at 138.43 92.71 0) + (hide yes) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) - (property "TC_DATAsheet" "~" + (property "Datasheet" "" (at 138.43 92.71 0) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (property "Description" "" (at 138.43 92.71 0) + (hide yes) + (show_name no) + (do_not_autoplace no) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "TC_DATAsheet" "" + (at 138.43 92.71 0) + (hide yes) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (pin "2" @@ -527,14 +606,14 @@ ) (instances (project "BREAD_Slice" - (path "/66043bca-a260-4915-9fce-8a51d324c687/8fe00014-2186-495f-9354-7853c82173d8" - (reference "J9") - (unit 1) - ) (path "/66043bca-a260-4915-9fce-8a51d324c687/1e1f4736-630c-4e72-b6eb-818a7a1a3b4e" (reference "J10") (unit 1) ) + (path "/66043bca-a260-4915-9fce-8a51d324c687/8fe00014-2186-495f-9354-7853c82173d8" + (reference "J9") + (unit 1) + ) ) ) ) @@ -542,13 +621,17 @@ (lib_id "Device:R_US") (at 129.54 83.82 0) (unit 1) + (body_style 1) (exclude_from_sim no) (in_bom yes) (on_board yes) + (in_pos_files yes) (dnp no) (uuid "7b9e25ca-2475-4aa0-adcf-000074a0cdb4") (property "Reference" "R7" (at 131.2672 82.6516 0) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) @@ -558,6 +641,8 @@ ) (property "Value" "10k" (at 131.2672 84.963 0) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) @@ -567,29 +652,45 @@ ) (property "Footprint" "Resistor_SMD:R_1206_3216Metric_Pad1.30x1.75mm_HandSolder" (at 130.556 84.074 90) + (hide yes) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) - (property "TC_DATAsheet" "~" + (property "Datasheet" "" (at 129.54 83.82 0) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (property "Description" "" (at 129.54 83.82 0) + (hide yes) + (show_name no) + (do_not_autoplace no) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "TC_DATAsheet" "" + (at 129.54 83.82 0) + (hide yes) + (show_name no) + (do_not_autoplace no) (effects (font (size 1.27 1.27) ) - (hide yes) ) ) (pin "1" @@ -600,14 +701,14 @@ ) (instances (project "BREAD_Slice" - (path "/66043bca-a260-4915-9fce-8a51d324c687/8fe00014-2186-495f-9354-7853c82173d8" - (reference "R7") - (unit 1) - ) (path "/66043bca-a260-4915-9fce-8a51d324c687/1e1f4736-630c-4e72-b6eb-818a7a1a3b4e" (reference "R8") (unit 1) ) + (path "/66043bca-a260-4915-9fce-8a51d324c687/8fe00014-2186-495f-9354-7853c82173d8" + (reference "R7") + (unit 1) + ) ) ) ) From 244140989e5c0ac849d8470ca141c00e2c9bdf64 Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Sun, 12 Jul 2026 14:02:49 -0400 Subject: [PATCH 02/10] fix(hw): un-swap the ERC and DRC Makefile targets KiBot's -s/--skip-pre SKIPS the named preflight, so `erc: kibot ... -s erc` skipped ERC and ran DRC, and `drc: ... -s drc` did the reverse. Both checks still ran, but every violation was reported under the wrong job name -- which is exactly what made a broken DRC look like a passing one. --- hardware/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hardware/Makefile b/hardware/Makefile index 32fc307..805a57f 100644 --- a/hardware/Makefile +++ b/hardware/Makefile @@ -8,10 +8,10 @@ PRJ_PCB=BREAD_Slice.kicad_pcb .PHONY: erc drc sch_fab pcb_fab erc_and_fab drc_and_fab erc: - $(KIBOT) $(EXTRA_OPS) -e $(PRJ_SCH) -b $(PRJ_PCB) -d $(OUT_DIR) -s erc -i + $(KIBOT) $(EXTRA_OPS) -e $(PRJ_SCH) -b $(PRJ_PCB) -d $(OUT_DIR) -s drc -i drc: - $(KIBOT) $(EXTRA_OPS) -e $(PRJ_SCH) -b $(PRJ_PCB) -d $(OUT_DIR) -s drc -i + $(KIBOT) $(EXTRA_OPS) -e $(PRJ_SCH) -b $(PRJ_PCB) -d $(OUT_DIR) -s erc -i sch_fab: $(KIBOT) $(EXTRA_OPS) -e $(PRJ_SCH) -b $(PRJ_PCB) -d $(OUT_DIR) \ From 3d0cb3e97c1d0498ff0e77009b7837f54e4170b8 Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Sun, 12 Jul 2026 14:02:49 -0400 Subject: [PATCH 03/10] fix(hw): fail DRC on schematic/PCB footprint mismatch KiCad's stock default severity for footprint_symbol_mismatch is `warning` (board_design_settings.cpp:189), and KiBot's DRC preflight only fails on errors. So `schematic_parity: true` ran the check and could never fail it -- a mismatch was reported on every CI run for months, buried in ~100 warnings, and every run went green. Raising it to `error` makes the parity gate real. --- hardware/BREAD_Slice.kicad_pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/BREAD_Slice.kicad_pro b/hardware/BREAD_Slice.kicad_pro index 84511a6..d6e830f 100644 --- a/hardware/BREAD_Slice.kicad_pro +++ b/hardware/BREAD_Slice.kicad_pro @@ -98,7 +98,7 @@ "footprint": "error", "footprint_filters_mismatch": "ignore", "footprint_symbol_field_mismatch": "warning", - "footprint_symbol_mismatch": "warning", + "footprint_symbol_mismatch": "error", "footprint_type_mismatch": "error", "hole_clearance": "error", "hole_near_hole": "error", From f900547ee15bdf4fdcdffbfc398f89f85079afe0 Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Sun, 12 Jul 2026 14:02:49 -0400 Subject: [PATCH 04/10] chore: gitignore KiCad 10 local history KiCad 10 added a Local History feature. On first save it creates /.history and calls git_repository_init() on it, so the directory is a nested git repository. KiCad writes a .gitignore inside it, but that governs KiCad's own history repo -- this repo still sees an embedded repository and reports it untracked. Committing it would add a broken gitlink. --- .gitignore | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5ccdd9b..abd1be7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,9 @@ fp-lib-table *.swp *-backups/ -working/ \ No newline at end of file +working/ + +# KiCad 10 local history: a nested git repo (git_repository_init on +# /.history). The .gitignore KiCad writes inside it governs +# KiCad's history repo, not this one. Committing it adds a broken gitlink. +.history/ From 86544950b55d451162a70d2bedc846e106fa709b Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Sun, 12 Jul 2026 14:32:05 -0400 Subject: [PATCH 05/10] chore: stop tracking .kicad_prl KiCad rewrites .kicad_prl on every project open -- open sheet, zoom level, selection filter. It is per-developer local state, not design intent, so tracking it means the working tree goes dirty every time anyone opens the board. --- .gitignore | 5 ++ hardware/BREAD_Slice.kicad_prl | 136 --------------------------------- 2 files changed, 5 insertions(+), 136 deletions(-) delete mode 100644 hardware/BREAD_Slice.kicad_prl diff --git a/.gitignore b/.gitignore index abd1be7..5b8569d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,8 @@ working/ # /.history). The .gitignore KiCad writes inside it governs # KiCad's history repo, not this one. Committing it adds a broken gitlink. .history/ + +# KiCad rewrites .kicad_prl on every project open (open sheet, zoom, selection +# filter). Per-developer local state, not design intent -- tracking it dirties +# the working tree every time anyone opens the board. +*.kicad_prl diff --git a/hardware/BREAD_Slice.kicad_prl b/hardware/BREAD_Slice.kicad_prl deleted file mode 100644 index 7704bf4..0000000 --- a/hardware/BREAD_Slice.kicad_prl +++ /dev/null @@ -1,136 +0,0 @@ -{ - "board": { - "active_layer": 0, - "active_layer_preset": "All Layers", - "auto_track_width": true, - "hidden_netclasses": [], - "hidden_nets": [], - "high_contrast_mode": 0, - "net_color_mode": 1, - "opacity": { - "images": 0.6, - "pads": 1.0, - "shapes": 1.0, - "tracks": 1.0, - "vias": 1.0, - "zones": 0.6 - }, - "prototype_zone_fills": false, - "selection_filter": { - "dimensions": true, - "footprints": true, - "graphics": true, - "keepouts": true, - "lockedItems": true, - "otherItems": true, - "pads": true, - "text": true, - "tracks": true, - "vias": true, - "zones": true - }, - "visible_items": [ - "vias", - "footprint_text", - "footprint_anchors", - "ratsnest", - "grid", - "footprints_front", - "footprints_back", - "footprint_values", - "footprint_references", - "tracks", - "drc_errors", - "drawing_sheet", - "bitmaps", - "pads", - "zones", - "drc_warnings", - "locked_item_shadows", - "conflict_shadows", - "shapes" - ], - "visible_layers": "ffffffff_ffffffff_ffffffff_ffffffff", - "zone_display_mode": 0 - }, - "git": { - "integration_disabled": false, - "repo_password": "", - "repo_type": "", - "repo_username": "", - "ssh_key": "" - }, - "meta": { - "filename": "BREAD_Slice.kicad_prl", - "version": 5 - }, - "net_inspector_panel": { - "col_hidden": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - false - ], - "col_order": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ], - "col_widths": [ - 162, - 147, - 91, - 67, - 91, - 91, - 91, - 71, - 91, - 91 - ], - "custom_group_rules": [], - "expanded_rows": [], - "filter_by_net_name": true, - "filter_by_netclass": true, - "filter_text": "", - "group_by_constraint": false, - "group_by_netclass": false, - "show_time_domain_details": false, - "show_unconnected_nets": false, - "show_zero_pad_nets": false, - "sort_ascending": true, - "sorting_column": 0 - }, - "open_jobsets": [], - "project": { - "files": [] - }, - "schematic": { - "hierarchy_collapsed": [], - "selection_filter": { - "graphics": true, - "images": true, - "labels": true, - "lockedItems": false, - "otherItems": true, - "pins": true, - "ruleAreas": true, - "symbols": true, - "text": true, - "wires": true - } - } -} From 7fb631b74b1629541712e1ad563a59d624e73939 Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Sun, 12 Jul 2026 14:32:05 -0400 Subject: [PATCH 06/10] ci: gate on board validation and run on pull requests - add the validate-board job: KiCad 10 format, a real (error-severity) DRC parity gate, every library reference resolving, un-swapped Makefile targets, and .history/.kicad_prl gitignored. Green means the board is done. - run on pull_request, not just push to main. Hardware checks previously ran only AFTER a change had merged: they could report that main was broken, but never stop it from breaking. A gate that only fires post-merge is a notification, not a gate. - pin bread-infra to v1.0.0 rather than @main, so a bread-infra commit cannot silently change what this PR is gated on. --- .github/workflows/docs-pipeline.yml | 30 +++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docs-pipeline.yml b/.github/workflows/docs-pipeline.yml index 29494e2..e5854b2 100644 --- a/.github/workflows/docs-pipeline.yml +++ b/.github/workflows/docs-pipeline.yml @@ -1,6 +1,19 @@ +# Copy to .github/workflows/docs-pipeline.yml in a board repo. +# +# The `pull_request` trigger is the important part. The fleet previously ran ERC +# and DRC only on `push: main` -- so hardware checks ran *after* a change had +# already merged. They could report that main was broken; they could never stop +# it from breaking. A gate that only fires post-merge is a notification, not a gate. + name: Docs Pipeline on: + pull_request: + paths: + - "hardware/**" + - "docs/**" + - "scripts/**" + - ".github/workflows/**" push: branches: [main] paths: @@ -11,18 +24,27 @@ on: workflow_dispatch: jobs: + # Fails if the board is not aligned with BREADS: KiCad 10 file format, a real + # (error-severity) schematic-parity gate, every library reference resolving, + # and un-swapped Makefile targets. Green here means the board is done. + validate-board: + uses: feastorg/bread-infra/.github/workflows/validate-board.yml@v1.0.0 + kibot: - uses: feastorg/bread-infra/.github/workflows/kibot-ci.yml@main + uses: feastorg/bread-infra/.github/workflows/kibot-ci.yml@v1.0.0 + # Publishing only makes sense for the canonical branch. gen-kibot-index: - uses: feastorg/bread-infra/.github/workflows/publish-kibot.yml@main + if: github.event_name != 'pull_request' + uses: feastorg/bread-infra/.github/workflows/publish-kibot.yml@v1.0.0 needs: [kibot] with: kibot_run_id: ${{ needs.kibot.outputs.kibot_run_id }} deploy-pages: - uses: feastorg/bread-infra/.github/workflows/deploy-pages.yml@main - needs: [gen-kibot-index] + if: github.event_name != 'pull_request' + uses: feastorg/bread-infra/.github/workflows/deploy-pages.yml@v1.0.0 + needs: [gen-kibot-index, kibot] with: kibot_run_id: ${{ needs.kibot.outputs.kibot_run_id }} commit_sha: ${{ needs.gen-kibot-index.outputs.kibot_index_sha }} From a2fe50b018b70c277ecc867895df01f516a1cfb8 Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Sun, 12 Jul 2026 14:38:34 -0400 Subject: [PATCH 07/10] fix: untrack hardware/.history (broken gitlink) KiCad 10's Local History runs git_repository_init() on /.history, so the directory is a nested git repository. It was committed as a gitlink, but there is no .gitmodules entry for it, so any checkout with submodules: true fails outright: fatal: No url found for submodule path 'hardware/.history' in .gitmodules which is why every KiBot CI job on this repo failed at checkout. Adding .history/ to .gitignore does not fix this: gitignore has no effect on already-tracked paths. The gitlink has to be removed from the index. --- hardware/.history | 1 - 1 file changed, 1 deletion(-) delete mode 160000 hardware/.history diff --git a/hardware/.history b/hardware/.history deleted file mode 160000 index c6426c7..0000000 --- a/hardware/.history +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c6426c7886559de3096bc65978e03d629be3d635 From 8ace516d1c3ea2bfaee003d8f4133ed6d738ef1d Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Sun, 12 Jul 2026 14:45:19 -0400 Subject: [PATCH 08/10] chore: gitignore KiCad 10 generated and lock files KiCad 10 adds design-block libraries and generates a design-block-lib-table alongside fp-lib-table and sym-lib-table. Like those, it holds absolute paths, so it is machine-local and generated -- never committed. Also ignores KiCad's project lock file (~.kicad_pro.lck), written whenever a project is open. The existing .gitignore predates KiCad 10 and covered neither, so simply opening a board left the working tree dirty. --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 5b8569d..8a15044 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,12 @@ fp-info-cache sym-lib-table fp-lib-table +# KiCad 10 adds design blocks; like the other lib tables it holds absolute +# paths, so it is machine-local and generated, never committed. +design-block-lib-table + +# KiCad's project lock file, written while a project is open (~.kicad_pro.lck) +*.lck # Ignore other temporary files *.tmp From 467c49d006f0323ecd385b8e4db9853e0f59a7ba Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Sun, 12 Jul 2026 15:18:24 -0400 Subject: [PATCH 09/10] ci: pin bread-infra v1.1.0 v1.1.0 adds the schematic-load check. kicad-cli prints 'Failed to load schematic' and still exits 0, so an unloadable schematic scored as zero ERC violations -- 34 boards were unopenable while every automated check said they were fine. validate-board now runs in the KiCad container and actually loads every sheet. --- .github/workflows/docs-pipeline.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docs-pipeline.yml b/.github/workflows/docs-pipeline.yml index e5854b2..359eb5e 100644 --- a/.github/workflows/docs-pipeline.yml +++ b/.github/workflows/docs-pipeline.yml @@ -28,22 +28,22 @@ jobs: # (error-severity) schematic-parity gate, every library reference resolving, # and un-swapped Makefile targets. Green here means the board is done. validate-board: - uses: feastorg/bread-infra/.github/workflows/validate-board.yml@v1.0.0 + uses: feastorg/bread-infra/.github/workflows/validate-board.yml@v1.1.0 kibot: - uses: feastorg/bread-infra/.github/workflows/kibot-ci.yml@v1.0.0 + uses: feastorg/bread-infra/.github/workflows/kibot-ci.yml@v1.1.0 # Publishing only makes sense for the canonical branch. gen-kibot-index: if: github.event_name != 'pull_request' - uses: feastorg/bread-infra/.github/workflows/publish-kibot.yml@v1.0.0 + uses: feastorg/bread-infra/.github/workflows/publish-kibot.yml@v1.1.0 needs: [kibot] with: kibot_run_id: ${{ needs.kibot.outputs.kibot_run_id }} deploy-pages: if: github.event_name != 'pull_request' - uses: feastorg/bread-infra/.github/workflows/deploy-pages.yml@v1.0.0 + uses: feastorg/bread-infra/.github/workflows/deploy-pages.yml@v1.1.0 needs: [gen-kibot-index, kibot] with: kibot_run_id: ${{ needs.kibot.outputs.kibot_run_id }} From 79bb015c8e725049debd8a15b4411178eec3c93d Mon Sep 17 00:00:00 2001 From: Cameron Brooks Date: Mon, 13 Jul 2026 00:45:16 -0400 Subject: [PATCH 10/10] fix(hw): use the canonical 2-layer KiBot config config.kibot.yaml had drifted into five variants across the fleet. Three differed only in trailing comments; the two real variants are 2-layer and 4-layer. Adopts bread-infra templates/config.kibot.2-layer.yaml, chosen from the board's actual copper layer count (2). Also pins bread-infra to v1.2.0, which adds a validate-board check that a board must plot every copper layer it has. --- .github/workflows/docs-pipeline.yml | 8 ++++---- hardware/config.kibot.yaml | 15 +++------------ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/workflows/docs-pipeline.yml b/.github/workflows/docs-pipeline.yml index 359eb5e..e99d57a 100644 --- a/.github/workflows/docs-pipeline.yml +++ b/.github/workflows/docs-pipeline.yml @@ -28,22 +28,22 @@ jobs: # (error-severity) schematic-parity gate, every library reference resolving, # and un-swapped Makefile targets. Green here means the board is done. validate-board: - uses: feastorg/bread-infra/.github/workflows/validate-board.yml@v1.1.0 + uses: feastorg/bread-infra/.github/workflows/validate-board.yml@v1.2.0 kibot: - uses: feastorg/bread-infra/.github/workflows/kibot-ci.yml@v1.1.0 + uses: feastorg/bread-infra/.github/workflows/kibot-ci.yml@v1.2.0 # Publishing only makes sense for the canonical branch. gen-kibot-index: if: github.event_name != 'pull_request' - uses: feastorg/bread-infra/.github/workflows/publish-kibot.yml@v1.1.0 + uses: feastorg/bread-infra/.github/workflows/publish-kibot.yml@v1.2.0 needs: [kibot] with: kibot_run_id: ${{ needs.kibot.outputs.kibot_run_id }} deploy-pages: if: github.event_name != 'pull_request' - uses: feastorg/bread-infra/.github/workflows/deploy-pages.yml@v1.1.0 + uses: feastorg/bread-infra/.github/workflows/deploy-pages.yml@v1.2.0 needs: [gen-kibot-index, kibot] with: kibot_run_id: ${{ needs.kibot.outputs.kibot_run_id }} diff --git a/hardware/config.kibot.yaml b/hardware/config.kibot.yaml index 6e41c38..83e01a7 100644 --- a/hardware/config.kibot.yaml +++ b/hardware/config.kibot.yaml @@ -70,7 +70,6 @@ outputs: dir: BoM options: format: CSV - # Here we avoid repetition using the bom_cols anchor defined for the HTML output columns: *bom_cols - name: "gerbers" @@ -78,7 +77,6 @@ outputs: type: gerber dir: Gerbers options: - # generic layer options exclude_edge_layer: true exclude_pads_from_silkscreen: false use_aux_axis_as_origin: false @@ -87,8 +85,6 @@ outputs: plot_footprint_values: true force_plot_invisible_refs_vals: false tent_vias: true - - # gerber options line_width: 0.1 subtract_mask_from_silk: false use_protel_extensions: false @@ -96,14 +92,9 @@ outputs: create_gerber_job_file: true use_gerber_x2_attributes: true use_gerber_net_attributes: true - layers: - layer: F.Cu suffix: F_Cu - # - layer: In1.Cu - # suffix: In1_Cu - # - layer: In2.Cu - # suffix: In2_Cu - layer: B.Cu suffix: B_Cu - layer: F.Paste @@ -154,8 +145,8 @@ outputs: type: position dir: Position options: - format: ASCII # CSV or ASCII format - units: millimeters # millimeters or inches + format: ASCII + units: millimeters separate_files_for_front_and_back: true only_smd: true @@ -175,4 +166,4 @@ outputs: dpi: 300 bottom: true mirror: true - output: "board_bottom.png" \ No newline at end of file + output: "board_bottom.png"