Repository navigation
Conversation
|
the actual boards required for qualification defined in issues need to be defined first |
The manufacturing job failed on PR absmach#75 because its expectations had never been established — I could not run that stage without a KiCad install, which is why the job was continue-on-error and labelled a baseline. CI has now run it on KiCad 10.0.6 and the uploaded artifact answers the question: hello mfg=pass placement_and_diff_pair mfg=pass env_logger mfg=fail sensor_logger mfg=fail the four reference boards mfg=unknown (their source gate fails first) env_logger and sensor_logger pass source and DRC and still cannot produce a fab package, so one `expect` cannot carry both answers. The matrix gains `expect_fab`, consulted only on a --fab run, and those two now record `pass` for the gate and `fail` for manufacturing. Boards whose fab baseline is not yet known carry `expect_fab = "unestablished"`: the observed value is written to the artifact so the next run says what to record, but it is not compared, because asserting a guess is how a gate teaches people to ignore it. That makes the job safe to promote, so continue-on-error is gone and it blocks. The runner also could not say *why* manufacturing failed — `check` reports the stage status but not the export's stderr, and an export that bails early never reaches the evidence report. On a failure it now re-runs the export directly and records the refusal lines, which is the difference between a result a reviewer can act on and one that only says "fail". Both failing boards uniquely share bme680_env and spst_tactile, whose footprints are the likely cause, but that is inference rather than evidence and the next artifact will name it.
The manufacturing job failed on PR absmach#75 because its expectations had never been established — I could not run that stage without a KiCad install, which is why the job was continue-on-error and labelled a baseline. CI has now run it on KiCad 10.0.6 and the uploaded artifact answers the question: hello mfg=pass placement_and_diff_pair mfg=pass env_logger mfg=fail sensor_logger mfg=fail the four reference boards mfg=unknown (their source gate fails first) env_logger and sensor_logger pass source and DRC and still cannot produce a fab package, so one `expect` cannot carry both answers. The matrix gains `expect_fab`, consulted only on a --fab run, and those two now record `pass` for the gate and `fail` for manufacturing. Boards whose fab baseline is not yet known carry `expect_fab = "unestablished"`: the observed value is written to the artifact so the next run says what to record, but it is not compared, because asserting a guess is how a gate teaches people to ignore it. That makes the job safe to promote, so continue-on-error is gone and it blocks. The runner also could not say *why* manufacturing failed — `check` reports the stage status but not the export's stderr, and an export that bails early never reaches the evidence report. On a failure it now re-runs the export directly and records the refusal lines, which is the difference between a result a reviewer can act on and one that only says "fail". Both failing boards uniquely share bme680_env and spst_tactile, whose footprints are the likely cause, but that is inference rather than evidence and the next artifact will name it.
The manufacturing job failed on PR absmach#75 because its expectations had never been established — I could not run that stage without a KiCad install, which is why the job was continue-on-error and labelled a baseline. CI has now run it on KiCad 10.0.6 and the uploaded artifact answers the question: hello mfg=pass placement_and_diff_pair mfg=pass env_logger mfg=fail sensor_logger mfg=fail the four reference boards mfg=unknown (their source gate fails first) env_logger and sensor_logger pass source and DRC and still cannot produce a fab package, so one `expect` cannot carry both answers. The matrix gains `expect_fab`, consulted only on a --fab run, and those two now record `pass` for the gate and `fail` for manufacturing. Boards whose fab baseline is not yet known carry `expect_fab = "unestablished"`: the observed value is written to the artifact so the next run says what to record, but it is not compared, because asserting a guess is how a gate teaches people to ignore it. That makes the job safe to promote, so continue-on-error is gone and it blocks. The runner also could not say *why* manufacturing failed — `check` reports the stage status but not the export's stderr, and an export that bails early never reaches the evidence report. On a failure it now re-runs the export directly and records the refusal lines, which is the difference between a result a reviewer can act on and one that only says "fail". Both failing boards uniquely share bme680_env and spst_tactile, whose footprints are the likely cause, but that is inference rather than evidence and the next artifact will name it.
The manufacturing job failed on PR absmach#75 because its expectations had never been established — I could not run that stage without a KiCad install, which is why the job was continue-on-error and labelled a baseline. CI has now run it on KiCad 10.0.6 and the uploaded artifact answers the question: hello mfg=pass placement_and_diff_pair mfg=pass env_logger mfg=fail sensor_logger mfg=fail the four reference boards mfg=unknown (their source gate fails first) env_logger and sensor_logger pass source and DRC and still cannot produce a fab package, so one `expect` cannot carry both answers. The matrix gains `expect_fab`, consulted only on a --fab run, and those two now record `pass` for the gate and `fail` for manufacturing. Boards whose fab baseline is not yet known carry `expect_fab = "unestablished"`: the observed value is written to the artifact so the next run says what to record, but it is not compared, because asserting a guess is how a gate teaches people to ignore it. That makes the job safe to promote, so continue-on-error is gone and it blocks. The runner also could not say *why* manufacturing failed — `check` reports the stage status but not the export's stderr, and an export that bails early never reaches the evidence report. On a failure it now re-runs the export directly and records the refusal lines, which is the difference between a result a reviewer can act on and one that only says "fail". Both failing boards uniquely share bme680_env and spst_tactile, whose footprints are the likely cause, but that is inference rather than evidence and the next artifact will name it.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
|
Rebuilt on current The old branch is preserved at Every expectation is measuredKiCad 10.0.6, KiCadRoutingTools v0.23.0 (
Nothing clears the gate, for four distinct reasons1. The declared design rules never reach the engine. All three real boards The exported The per-netclass clearance and the minimum via diameter live in the 2. The engine exits 1 on an empty board. 3. Manufacturing refuses on unreviewed parts. Every board whose source 4. The four imported designs fail the source gate — which is what they are One test change worth calling out
One regression caught before this went upThe commit that adds
The remaining local failures — |
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
|
I found three issues that should be addressed before merging:
|
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
rodneyosodo
left a comment
There was a problem hiding this comment.
LGTM. The router forwarding is the right shape: --router is Option (unset means “don't mention it”, so an env/default selection survives), and child_args takes clap's own value name rather than RouterEngine::as_str — which is the freerouting vs free-routing trap. Stage budgets via SYNTH_CHECK_*_TIMEOUT_SECS with the old defaults preserved is a clean fix, and the qualification test is genuinely #[ignore]d so the --ignored job is not vacuous.
Only red is Lint and Build, and that is the repo-wide rustls-webpki 0.103.15 advisory (RUSTSEC-2026-0381/0382) — main is red for the same reason and #248 fixes it. Nothing to change here; rebase after #248 and the check clears.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
…using it Completing the W5500's pin map renamed its digital supply from `vcc` to `vdd`, pin 28, and three fixtures kept connecting `vcc`. The pin no longer exists, so each of them reported `E-SYNTH-COMP-002` for an undefined pin and `E-SYNTH-CONNECT-002` for the supply that never arrived. CI stayed green throughout, which is the part worth noting. The ERC fixture harness asserts that a `<CODE>__name.synth` file emits the code it is named for and allows any others, so `E-SYNTH-RESET-001` and `E-SYNTH-SPI-002` kept firing and nothing failed -- while the boards they fire on had become different boards, with an unpowered Ethernet controller and two extra errors each. A fixture that no longer isolates its own case stops being evidence for it. Both codes are gone now and the named ones remain. The two `U1.vcc` left in the SPI fixture are a `ch340g`, which does declare `vcc`. The harness weakness is the same one the qualification matrix had on absmach#75, where `expect_source_codes` was a subset check: a declared list that is a floor rather than a set lets a case decay quietly. Worth fixing there too, and out of scope here.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
|
[P1] Accidental fixture/result changes are still not automatically detected. The matrix records registry and artifact hashes, but compares them against no versioned baseline. A board’s schematic, PCB, BOM, or registry data can change while preserving the same statuses and diagnostics, and CI will still pass. Issue #45 explicitly requires detection of accidental fixture/result changes. Please add a committed manifest/golden comparison for stable inputs or outputs, or explicitly narrow the issue acceptance criteria. |
…using it Completing the W5500's pin map renamed its digital supply from `vcc` to `vdd`, pin 28, and three fixtures kept connecting `vcc`. The pin no longer exists, so each of them reported `E-SYNTH-COMP-002` for an undefined pin and `E-SYNTH-CONNECT-002` for the supply that never arrived. CI stayed green throughout, which is the part worth noting. The ERC fixture harness asserts that a `<CODE>__name.synth` file emits the code it is named for and allows any others, so `E-SYNTH-RESET-001` and `E-SYNTH-SPI-002` kept firing and nothing failed -- while the boards they fire on had become different boards, with an unpowered Ethernet controller and two extra errors each. A fixture that no longer isolates its own case stops being evidence for it. Both codes are gone now and the named ones remain. The two `U1.vcc` left in the SPI fixture are a `ch340g`, which does declare `vcc`. The harness weakness is the same one the qualification matrix had on absmach#75, where `expect_source_codes` was a subset check: a declared list that is a floor rather than a set lets a case decay quietly. Worth fixing there too, and out of scope here.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
…using it Completing the W5500's pin map renamed its digital supply from `vcc` to `vdd`, pin 28, and three fixtures kept connecting `vcc`. The pin no longer exists, so each of them reported `E-SYNTH-COMP-002` for an undefined pin and `E-SYNTH-CONNECT-002` for the supply that never arrived. CI stayed green throughout, which is the part worth noting. The ERC fixture harness asserts that a `<CODE>__name.synth` file emits the code it is named for and allows any others, so `E-SYNTH-RESET-001` and `E-SYNTH-SPI-002` kept firing and nothing failed -- while the boards they fire on had become different boards, with an unpowered Ethernet controller and two extra errors each. A fixture that no longer isolates its own case stops being evidence for it. Both codes are gone now and the named ones remain. The two `U1.vcc` left in the SPI fixture are a `ch340g`, which does declare `vcc`. The harness weakness is the same one the qualification matrix had on absmach#75, where `expect_source_codes` was a subset check: a declared list that is a floor rather than a set lets a case decay quietly. Worth fixing there too, and out of scope here.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
…using it Completing the W5500's pin map renamed its digital supply from `vcc` to `vdd`, pin 28, and three fixtures kept connecting `vcc`. The pin no longer exists, so each of them reported `E-SYNTH-COMP-002` for an undefined pin and `E-SYNTH-CONNECT-002` for the supply that never arrived. CI stayed green throughout, which is the part worth noting. The ERC fixture harness asserts that a `<CODE>__name.synth` file emits the code it is named for and allows any others, so `E-SYNTH-RESET-001` and `E-SYNTH-SPI-002` kept firing and nothing failed -- while the boards they fire on had become different boards, with an unpowered Ethernet controller and two extra errors each. A fixture that no longer isolates its own case stops being evidence for it. Both codes are gone now and the named ones remain. The two `U1.vcc` left in the SPI fixture are a `ch340g`, which does declare `vcc`. The harness weakness is the same one the qualification matrix had on absmach#75, where `expect_source_codes` was a subset check: a declared list that is a floor rather than a set lets a case decay quietly. Worth fixing there too, and out of scope here.
…using it Completing the W5500's pin map renamed its digital supply from `vcc` to `vdd`, pin 28, and three fixtures kept connecting `vcc`. The pin no longer exists, so each of them reported `E-SYNTH-COMP-002` for an undefined pin and `E-SYNTH-CONNECT-002` for the supply that never arrived. CI stayed green throughout, which is the part worth noting. The ERC fixture harness asserts that a `<CODE>__name.synth` file emits the code it is named for and allows any others, so `E-SYNTH-RESET-001` and `E-SYNTH-SPI-002` kept firing and nothing failed -- while the boards they fire on had become different boards, with an unpowered Ethernet controller and two extra errors each. A fixture that no longer isolates its own case stops being evidence for it. Both codes are gone now and the named ones remain. The two `U1.vcc` left in the SPI fixture are a `ch340g`, which does declare `vcc`. The harness weakness is the same one the qualification matrix had on absmach#75, where `expect_source_codes` was a subset check: a declared list that is a floor rather than a set lets a case decay quietly. Worth fixing there too, and out of scope here.
The DRC stage had a hardcoded 30s budget. A real board's DRC runs a full place-and-route: env_logger takes 18s on a release build but 205s on a debug one, so `synth check` reported drc=unknown — correctly, since no evidence was produced, but uselessly, because nothing was actually wrong. SYNTH_CHECK_SOURCE_TIMEOUT_SECS, SYNTH_CHECK_DRC_TIMEOUT_SECS and SYNTH_CHECK_FAB_TIMEOUT_SECS let a release gate buy the time its boards need. The defaults are unchanged and are adequate for the corpus on a release build. Mirrors SYNTH_KICAD_CLI_TIMEOUT_SECS on the native stages.
`synth check` does not route; it spawns `synth drc` and `synth export-kicad`, and those own the engine. There was no way to tell it which one to use, so a release gate could not select one. With no engine available `synth drc` refuses to route, emits no DRC counts, and the stage reports `unknown` — not a pass. The gate then fails for every board, the empty one included, for a reason that is not about any design. `--router` and `--kicad-routing-tools-repo` now travel to both children. `--router` is an Option with no default, and unset means "do not mention it" rather than "use the default". The stages already have a default and read the environment, and an explicit argument outranks both, so forwarding one unasked replaces a selection made another way. Giving this flag a default value is not the harmless thing it looks like: it made a KiCadRoutingTools checkout installed through `KICAD_ROUTING_TOOLS_REPO` come back as a missing Java runtime, because `check` was quietly telling its children to use freerouting instead. The engine's name in the child's argument list is clap's value for the variant, not `RouterEngine::as_str`. Those two disagree — `as_str` says `freerouting` where the CLI accepts `free-routing` — and a child invoked with the wrong spelling is rejected by its own parser.
qualification/matrix.toml declares, per board, a capability class, why the board is in the corpus, and the result the deterministic release command must produce. The test runs `synth check <board> --json` over the corpus and fails when any board stops matching its declaration — in either direction, so a launch claim cannot grow without someone declaring it. Eight boards, classes A through D, four positive and four negative. Every expectation was measured on KiCad 10.0.6 with KiCadRoutingTools v0.23.0 on a release build, and the rationale for each records what was observed rather than what was hoped for. No board clears the gate today, and four distinct causes are named: the per-netclass clearance and minimum via diameter are not expressed in the exported .kicad_dru, so the engine routes to the global floor and kicad-cli pcb drc rejects its own board's rules; the engine exits 1 on a board with nothing to route; no shipped part carries a reviewer, so manufacturing refuses; the four imported reference designs fail the source gate, which is what they are in the corpus to prove. Positive and negative are read off `status`, not off `expect`. `expect` is a measurement: a board whose copper is complete can still fail at DRC, and requiring some row to declare `expect = "pass"` would force the matrix to claim a pass nobody had observed. The release gate itself is `#[ignore]`d — each board runs a full place-and-route — and runs in its own job. A second test keeps the corpus well formed on every shard: paths resolve, ids are unique, every negative names the diagnostics it expects, and every class is represented.
Two jobs, both blocking. `Qualification matrix` runs the release command over the corpus; `Qualification matrix (manufacturing)` runs it again with the export gate, which needs KiCad present to produce any evidence at all. Both upload target/qualification/results.json whether they pass or fail, so a result is attributable to a compiler change, a registry edit or a different KiCad rather than being a mystery. Three things the jobs cannot do without: KiCad itself, from the 10.0.6 PPA, plus the stock symbol and footprint libraries. Without them most registry parts come back `unknown` instead of pass or fail. An engine. Synth does not route copper; it drives one and validates what it wrote. With none available `synth drc` refuses to route, emits no counts, and the DRC stage reports `unknown` — not a pass — so every board fails for a reason that has nothing to do with any design. KiCadRoutingTools, pinned to v0.23.0 because "whatever main is today" is not a reproducible run. A release build. A debug DRC on a real board takes roughly ten times as long and overruns the stage budget, which reports `unknown` correctly but uselessly. The toolchain is pinned to 1.98.1 and the cache keyed on rust-toolchain.toml, matching ci.yml; `@stable` disagrees with the toolchain file this repo pins. Path-filtered to the compiler crates, the registry and the corpus, so it does not run on documentation-only changes. GitHub reports a filtered-out workflow as missing rather than passing, which the handoff doc warns about for anyone making this a required check.
A board that passes the matrix has passed the compiler, the rule checks and the exported-artifact checks. That is not the same as being fabricable, and the table here names the six areas — visual, assembly, signal integrity, power integrity, thermal, fabrication — that need a human with PCB experience looking at the artifacts, because no amount of green CI substitutes for them. Promotion from `experimental` to `qualified` is a review with a recorded verdict per area, not a code change. Also records where the corpus stands: every row reads `fail`, and the four causes are distinct and named. A matrix whose rows all read `fail` is doing its job, and the day one cause is fixed the gate will say so by failing in the other direction.
The path filter listed every crate that can change a board's release output and then stopped at the workspace boundary, so `Cargo.toml`, `Cargo.lock` and `rust-toolchain.toml` could all change what the compiler emits for all seventeen boards without the gate running once. This is not hypothetical. The webpki advisory fix is a lockfile-only commit that touches no path the filter named, and GitHub reports a filtered-out workflow as missing rather than as passing, so nothing would have said so.
The check was a subset: every code in `expect_source_codes` had to be reported, and anything else the board said was ignored. So an `unsupported` board could acquire a second, unrelated defect and still pass the gate, as long as the code already written down kept firing. That is the direction this matrix exists to catch -- a board's behaviour changing without anyone declaring it -- and it was the one direction the diagnostics were not checked in. Both differences are now reported. A code that appears and a code that vanishes each mean the compiler says something new about a board nobody re-declared, and the second is the easier one to miss: a rule that stops firing looks like silence, not like a failure. This tightens nothing today. Against CI's own `results.json` for all seventeen boards, every declared list already equals the observed set exactly, so the change is free and only the hole closes. Only error-level codes are compared, which `source_codes` already filters to, so a new warning still does not fail the gate.
…gate them `deterministic_artifacts` called the schematic, PCB and BOM hashes "the drift detector", and the handoff document said the matrix "compares" them. Neither is true: the hashes are collected into `results.json` and nothing ever reads them back. A fixture or output change could therefore pass unnoticed, as long as the status and the diagnostics held -- while the comment claimed it could not. Pinning them is the wrong fix, for a reason worth writing down rather than rediscovering. The `.kicad_pcb` carries the copper an external routing engine laid, so its hash moves when that engine is upgraded even though no board, no part and no compiler changed. A gate that fails for a reason no reviewer can act on is a gate people learn to ignore, which is the same argument the Gerber timestamps already lost. So the claim goes, and both places now say what the hashes are for: a human comparing two runs' artifacts, and attribution when a result moves. What the matrix actually gates on is the declared verdict -- `expect`, `expect_fab` and `expect_source_codes`, the last of them now an exact set.
Two RP2350s on six layers, each on its own native USB, each pair at 90 ohm with `max_skew 0.15mm` and `couple tight`, and `match_length 0.5mm` on the USB class holding all four nets to one length. Two independent pairs on one board is what case 3 asks about. Adapted from the board in absmach#75, whose header carried a STATED LIMITATION saying SynthSpec had no length-matching or skew syntax so these budgets could not be declared. They can now, and these are the budgets that limitation stood in for. Two substitutions, both forced. The second endpoint is an RP2350 rather than the ESP32-C3 the design was written around: that part names a symbol and footprint absent from KiCad 10 and declares USB on two pins the module does not have (absmach#146), so a board using it cannot export. And the two keepouts are gone — a declared keepout reaches no exported board (absmach#142), and even when it does a KiCad keepout is unconditional and would forbid the pair it protects. What the README says plainly is where the budgets stop. Neither reaches KiCad (absmach#147) and neither is measured in this build, so the board states them and nothing yet holds it to them.
…using it Completing the W5500's pin map renamed its digital supply from `vcc` to `vdd`, pin 28, and three fixtures kept connecting `vcc`. The pin no longer exists, so each of them reported `E-SYNTH-COMP-002` for an undefined pin and `E-SYNTH-CONNECT-002` for the supply that never arrived. CI stayed green throughout, which is the part worth noting. The ERC fixture harness asserts that a `<CODE>__name.synth` file emits the code it is named for and allows any others, so `E-SYNTH-RESET-001` and `E-SYNTH-SPI-002` kept firing and nothing failed -- while the boards they fire on had become different boards, with an unpowered Ethernet controller and two extra errors each. A fixture that no longer isolates its own case stops being evidence for it. Both codes are gone now and the named ones remain. The two `U1.vcc` left in the SPI fixture are a `ch340g`, which does declare `vcc`. The harness weakness is the same one the qualification matrix had on absmach#75, where `expect_source_codes` was a subset check: a declared list that is a floor rather than a set lets a case decay quietly. Worth fixing there too, and out of scope here.
What type of PR is this?
This is a feature
What does this do?
Gates eight reference boards against
qualification/matrix.toml, and letscheckpick the router its stages use.Which issue(s) does this PR fix/relate to?
Resolves #45. Records #98 and #51 as the causes four boards fail.
Have you included tests for your changes?
Yes:
qualification_matrix.rsgates the corpus, and a second test keepsmatrix.tomlwell formed.Did you document any new/modified features?
Yes:
docs/qualification-handoff.mdcovers statuses, the router requirement and what review automation cannot replace.Notes
Rebuilt on current main; its boards and parts now live in #195, #196, #103 and #110. Measurements in a comment.