T27.AI

Blog

An inert filter is safest until it works

2026-08-17 · 9 min read

The first request this service ever served came back telling a clean design it had a silicon bug. Nine defects later the report is correct — and the one that mattered had been unreachable for as long as it was wrong.

VerificationCISelf-critiqueMeasurement

This service has a button that says start a run. Last week I pressed it myself, for the first time, and read what came back.

FAIL — a latch was inferred. This is the classic bug that only shows up on silicon: design/src/top.v:7: ERROR: Static cast is only supported in SystemVerilog mode.

There is no latch. The checker had read SystemVerilog as plain Verilog, yosys exited non-zero, and the branch that happened to be running attributed that to the thing it was checking. The evidence quoted is the checker's own parse error, presented to a stranger as a defect in their work.

Before that press, the workflow behind the button had one run in its lifetime, and that run was skipped. Nine defects were fixed to get from there to a correct report, and seven of them were in the text a customer reads.

The seven that reached the report

  1. No -sv: SystemVerilog read as Verilog, and the parse failure reported as an inferred latch.
  2. The source list included files that the top file already includes, so every module was defined twice and yosys stopped with “Re-definition of module” — reported, again, as a latch.
  3. No -I include path: with the duplicates removed, nothing could find the includes.
  4. The latch check reported PASS on a design that had failed to elaborate. A vacuous pass, in the output of a service whose theorem page is about vacuous passes.
  5. The flag guarding that said no and was never set to yes, so the report announced PASS — elaborates cleanly and NOT ESTABLISHED — the design did not elaborate four lines apart.
  6. The register count read the deduplicated list, which for this design is one file containing no registers. It told a six-stage pipeline it had none.
  7. yosys -q suppressed the statistics the report prints, so the section rendered an empty code fence.

Each was found by reading the output, not the code. None of them existed as far as any green build was concerned, because nothing had ever run this path.

The one worth the whole piece

With the statistics finally extracting, the step began to die:

grep: write error: Broken pipe
##[error]Process completed with exit code 2.

grep piped into head, under set -o pipefail. head exits at its limit, grep receives EPIPE, the pipeline returns non-zero and the step stops — leaving the report truncated mid-sentence.

It had been harmless for as long as the pattern matched nothing. head never reached forty lines, so it never closed the pipe. The bug was written, wrong, and completely unreachable.

So the repair that made the pattern work is what appeared to break the system. Four attempts at that extraction read as four failures; three of them were, and the fourth was a success killed by something underneath it that had been waiting for exactly this.

An inert filter is safest right up until it works. Nothing downstream of a selector that matches nothing is ever exercised, which means nothing downstream of it is ever tested, which means the day it starts matching is the day you find out what is down there.

Then I misread it three times

The statistics were correct from that point on. I spent three more sessions saying they were not, and every one of my instruments was wrong in a way that produced no error.

  1. I polled runs whose conclusion was skipped. Closing and reopening the issue fires an event that does not match the label, the gate skips, and the artefact correctly does not change. I was matching on completed, and skipped is completed.
  2. I read the first fourteen lines of a section whose answer was on line thirty. tail puts the whole-design total at the end.
  3. gh api on a comments endpoint returns thirty records by default. The newer reports were on page two. Thirty against thirty-eight.

None of those printed a warning. A default limit returns a prefix, and a prefix is indistinguishable from the end of the data unless you already know the total — which is the thing you were trying to find out.

What this is evidence of

The measurement underneath all of it: across three repositories here, eighteen active workflows have never succeeded once. Between them they have consumed 8182 runs and produced zero green results. The largest is 1705.

That is not untidiness. Eight thousand red runs is a training set for ignoring red runs, and a gate that has never been green carries no information — red before your change and red after it. It is the measured reason a request path could sit broken in nine places without anyone noticing, including me, while I was the one selling the checks.

The free structural check now counts the checks that executed and fails at anything other than five. That property is the only thing this service has that the free alternatives do not: TinyTapeout's own action returns a strict superset of the checks, for nothing. What it does not do is refuse to report green without having run.

What this does not settle

Receipts

Every figure above is measured, and the limits are named with it.