svGolf asks for two things about each design you submit: a Lean proof that it meets the task's trusted statement under the ls-sv semantics, and a score from a pinned Yosys flow. The checker refuses a design or a proof when the proof and the score could be about different circuits, or when the evidence could be forged. This document lists every refusal the checker makes, why, and what you can write instead, then says which refusals are meant to go away and which never will.
It has two halves that are easy to confuse. The checker's own stages (source gate, banlist, fourstate, statement, axioms, score,
post-synthesis check) are described below stage by stage. The language tier (ls-sv, the semantics your proof is about) refuses
more constructs than the checker does; those show up as extract or design failures and are summarised near the end and listed in
full in docs/sv-refusals.md of the lean-surfaces repository.
This file is generated from docs/sv-refusals.json of lean-surfaces by tools/gen_sv_refusals.py and is written to be read without
the rest of that repository: links to lean-surfaces documents are given as repository paths.
How to read this page
Every restriction has one of four states; a row marked proposed carries a classification that is not yet confirmed.
| state | meaning |
|---|---|
| refused-now | refused loudly today and this is the intended behaviour at this version; no future is recorded |
| later | refused today and intended to be ALLOWED once a stated prerequisite is met (a proof obligation, a model extension, a checker stage, a measured benefit); the row names the prerequisite and, where recorded, the plan item |
| never | refused by principle and not to be allowed: no meaning can be given that is the same for the proof (ls-sv / slang) and the score (Yosys netlist), or it is not synthesizable hardware, or it would let the proved design differ from the scored one; the row names the principle |
| refused-pending | decided and in flight, not yet in the code (transitional): the row names the decision; the completeness check allows its tag to be absent and warns once it appears |
The never rows rest on these principles:
| principle | statement |
|---|---|
P-SAME-PROGRAM | Proof and score read ONE program: a construct the two frontends (slang for the proof, Yosys for the score) read differently, that slang rejects (decided 2026-10-31: "a good reason to reject"), or that Yosys cannot read, cannot be admitted. |
P-NO-TOOL-DIRECTIVES | No construct whose meaning is a synthesis-tool directive: pragmas, attributes, `ifdef SYNTHESIS / YOSYS, translate_off comments. |
P-NO-TIME | No delays, timing, wall-clock or time-dependent behaviour in a combinational task: synthesis ignores them, so the proved (timed) behaviour and the scored netlist would differ. |
P-SYNTHESIZABLE | Not synthesizable hardware, for a design task whose scored artifact is a gate netlist from Yosys read_verilog -sv: class declarations, dynamic memory (queues, dynamic and associative arrays), string variables and parameters, real variables, fork/join, program blocks. The frontend does not read them (measured by harness/sv/tools_split_audit/yosys_frontend.py; a constant real in a parameter expression IS read), so they are out of scope for now; revisit if a different synthesis frontend is adopted (decided 2026-10-31, about classes: a future synthesis frontend may change that). Host access (file I/O, system calls, DPI) is refused by its own policy, not by this principle. |
P-NO-SILENT-PICK | The PIPELINE tools split (slang / ls-sv for the proof, Yosys for the score, Verilator and Xcelium as simulators give different values for the same text) and no consistent reading exists: refuse loudly, never decide for one side. A split in which only Icarus differs is not a refusal but a declared departure (decided 2026-10-31), and neither is one in which only Xcelium differs from LRM text that slang, Verilator and Yosys follow (decided 2026-11-01: \a \v \f). |
P-DETERMINISM | A combinational result must be a function of the inputs, whatever the schedule: randomness is refused, and so is a design with no completed run (it never satisfies Terminates2). Where the LRM itself leaves the schedule open (IEEE 4.7) the ruling of 2026-10-31 is that the construct is to be modelled and the golfer proves the output the same in every legal run, not that it is refused for that reason. A simulator-versus-synthesizer mismatch (self-reading always_comb: the LRM's answer is deterministic, the synthesizers compute another) is not that case. |
P-TRUSTED-BASE | (added by the register authors, to confirm) The evidence a verdict rests on (checker, trusted statement, generated Goal, axiom set, toolchain) must not be changeable by the submission; a proof is accepted by the kernel from the allowed axioms alone. |
P-NO-HOST-ACCESS | (decided 2026-10-31 for the READING of files; confirmed 2026-11-01 for the rest of the family: "Yes, don't allow this. It's not safe.") The SystemVerilog text of a submission cannot read or write host files, run programs, read the environment or call native code: the checker runs untrusted text on the organizer's machine (Yosys's read_verilog itself opens the file of a $readmemh). The Lean file of a submission is not scanned for it: it is CONTAINED, by the checker's sandbox around every process that runs submitted text (WP-S, 2026-10-31; known hole "Host access from the Lean file of a submission", mitigated). |
The pipeline: where a refusal can happen
check.py runs the stages in this order and stops at the first that refuses.
| stage | what it does | refuses |
|---|---|---|
task | reads task.json | unknown keys, a status other than ready / draft, a ready task without a reference proof |
imports | checks the Lean file's imports against an allowlist | any import outside Init, Std, Lean, LeanModels.Sv.*, SvGolf.*, tasks.*, Submission.Design and the Mathlib packages (Mathlib, Batteries, Aesop, Qq, ProofWidgets, Plausible, ImportGraph, LeanSearchClient), read from the header as Lean reads it (a comment between two imports separates them) and checked again against the imports Lean recorded in the compiled solution; a second file; module / prelude headers; a module no package has a source for (a stale name) is a rejection, a package or module missing from the machine (not built, not on the search path, a checkout not at the pinned revision) is an infrastructure error |
source | score/srcgate.py: makes the proved program and the scored program ONE text and strips every comment | preprocessor directives, attributes, synthesis-pragma comments, unique / unique0 / priority, x/z/? literal digits, odd characters, host access (file, process, environment, dump and DPI constructs) |
extract | the pinned lean-surfaces extractor reads the canonical text | whatever the extractor refuses (the language tier), a wrong top module, a parameter list that differs from the task's |
banlist | score/banlist.py: pyslang front check plus a Yosys backstop | the run-time operators the task bans, in every disguise Yosys turns into the same cell; for a task that bans an operator, also every error of slang's analysis pass (as fourstate) |
fourstate | score/fourstate.py at the scoring point | any way an x or z value can arise: constants with unknown bits, out-of-range selects, undriven or conflicting nets, logic loops, latches, and every error of slang's analysis pass (two writers of one variable, mixed continuous and procedural assignment, writing an input port, an always without timing control) |
trusted | builds the trusted svGolf modules and the ls-sv evaluator (not timed) | infrastructure only |
design | compiles the generated Submission.Design with load_design_sv2 | loader refusals: cross-checks against slang, the one-driver rule, self-reading always_comb, select guards, the multi-point probe check |
solution | compiles Solution.lean under the task's time limit | a proof that does not compile in time |
integrity | snapshots the trusted files before and after the solution builds | a solution whose build changes a staged or trusted file (a tripwire, not a sandbox) |
comparator | check/Comparator.lean: the kernel accepts theorem _ : Submission.Goal := Submission.solution | a missing theorem, a proof of another statement, axioms outside the task's set, extern / implemented_by |
leanchecker | kernel replay of the submission's modules | declarations added with the kernel check switched off |
score | score/score.py: the two-stage Yosys + ABC flow and the GE table | a netlist with cells the table does not price (latches, flip-flops), a non-deterministic flow, a design over the depth budget |
postsynth | check/postsynth.py: ls-sv and the scored netlist (Verilator) must agree on every vector of the budget | any disagreement, x/z in ls-sv outputs, a netlist Verilator will not build; a rejection is a bug report |
Policies
- Combinational tasks only for now. Clocked designs, registers and pipelines are refused until the sequential layer exists (decided 2026-10-29).
- Where the pipeline tools split, the design is refused, never decided for one side: slang / ls-sv (the proof), Yosys (the score), Verilator and Xcelium are compared, and a shape they read differently is a loud refusal. A difference that only Icarus shows is a declared departure, not a refusal (decided 2026-10-31), and so is one that only Xcelium shows against LRM text the other tools follow (decided 2026-11-01:
\a \v \fare admitted). - A program slang rejects is rejected, and LRM-legal nondeterminism is to be modelled rather than forbidden (decided 2026-10-31). No construct is admitted on the second ruling yet: the model does not exist, and the audit found that the rows it was first thought to cover are not LRM nondeterminism (self-reading
always_comb: the LRM's answer is deterministic and the synthesizers compute another; settling combinational loops: refused by fourstate, and some hold state). - Everything the proof and the score could read differently is refused exactly, before any proof is tried (the x/z rules, the source gate), and the post-synthesis check is the backstop for the disagreements nobody has found.
- No other restrictions. Beyond the per-task operator banlist and these safety gates, write any SystemVerilog ls-sv can load.
- Contestants may add features to ls-sv by pull request, and the maintainers approve every such PR. See 'How a refusal becomes allowed'.
- Proofs are symbolic and parametric (for all N).
bv_decideis not supported for now (it runs an external SAT solver, which the checker does not support: decided 2026-11-01);native_decideis banned in submissions. - No host access in the SystemVerilog text. A submitted design cannot read host files (decided 2026-10-31); by the same principle (proposed, to confirm) it cannot write them, run programs, read the environment, write dumps or call native code. The source gate refuses the constructs; the console tasks (
$display,$write,$finish, ...) stay. The Lean file is not covered (see the limits).
Stage source: one program for both tools
Yosys and slang (the frontend your proof is about) read the same file differently when it contains directives, attributes or pragma comments. The gate refuses those and hands every later stage one canonical text with all comments stripped. It also refuses every construct that reaches the machine the checker runs on ($readmemh, $fopen, $system, $value$plusargs, $dumpfile, DPI, ...): Yosys opens a $readmemh file itself while it reads the design.
| refused | why | state |
|---|---|---|
| conditional compilation on a tool macro e.g. `` `ifdef SYNTHESIS / `ifdef YOSYS / `ifndef ... `else ... `endif `` | Yosys predefines SYNTHESIS and YOSYS, slang does not: the proof reads one branch, the score the other (a constant, 0 GE). | never P-NO-TOOL-DIRECTIVES (proposed) |
user macros: `define, `undef, a use of a defined macro, `__FILE__e.g. `` `define W 4 `` with a use of `` `W ``; `` `undef FOO ``; a conditional on a user macro | The gate hands the canonical text UNEXPANDED to slang, the extractor and Yosys, so it cannot show that three preprocessors expand a user macro identically. | later none recorded; proposed prerequisite: the gate expands user macros itself and hands ONE preprocessed text to every consumer (SYNTHESIS / YOSYS conditions stay refused) |
`includee.g. `` `include "foo.vh" `` | The included file is a second source the gate, the staging directory, the hash record and the proof never see; include paths differ per tool. | later none recorded; proposed prerequisite: multi-file designs with every included file gated, staged and hashed |
`pragmae.g. `` `pragma protect begin `` | A `pragma is a tool directive by definition: each tool acts on the pragma names it knows. | never P-NO-TOOL-DIRECTIVES (proposed) |
other directives: `line, `celldefine, `resetall, `begin_keywords; `default_nettype other than none | wire; a malformed `timescalee.g. `` `line 10 "x.sv" 0 ``; `` `default_nettype uwire ``; `` `timescale 1ns `` | Only `timescale and `default_nettype none|wire are on the whitelist: each other directive changes how one tool lexes or elaborates, with no counterpart the gate can show the other tool shares. | refused-now |
attributes (* ... *) and {* ... *}e.g. (* keep *) wire t = a & b; (* full_case *) before a case; even an empty (* *); {* keep *} before a module | Yosys gives attributes (full_case, parallel_case, keep, nosync, keep_hierarchy, blackbox, ...) a synthesis meaning; slang and IEEE simulation give them none. always @(*) still passes the gate. | never P-NO-TOOL-DIRECTIVES (proposed) |
| synthesis-pragma comments e.g. // synopsys translate_off; /* synthesis keep */; // pragma foo; and the words translate_off, translate_on, full_case, parallel_case anywhere in ANY comment | Yosys's lexer reads these comments as directives; slang ignores comments. Refusing is a courtesy; STRIPPING every comment is the guarantee. A comment that merely names full_case is refused too (over-refusal). | never P-NO-TOOL-DIRECTIVES (proposed) |
unique, unique0, prioritye.g. unique case (a[0]) ... endcase; unique if (...) ... else ...; unique0 case ...; priority case ... | Yosys turns unique into full_case + parallel_case, unique0 into parallel_case, priority into full_case (synthesis don't-cares); ls-sv runs the first matching arm. A unique case that never matches scores 0 GE. | later WP5: ls-sv turns a violated qualifier claim into a run-time error on the settled state so Terminates2 forces the proof obligation; the gate refusal goes once WP5 lands |
| non-ASCII or control characters outside comments; a file that is not UTF-8 e.g. string s = "héllo"; a form feed; a stray 0xFF byte | Identifier and string lexing of non-ASCII text is each tool's business; the gate refuses rather than assume the tools agree (non-ASCII INSIDE a comment passes). | refused-now |
an unterminated block comment; // or /* inside a string or an escaped identifier; text slang skips (a syntax error)e.g. /* never closed; string s = "// not a comment"; assign y = a & b @@ ; | No comment boundary exists, or Yosys's lexer might see a comment that slang does not; skipped or disabled text may still be parsed by Yosys. | refused-now |
memory-file tasks $readmemh, $readmemb, $writememh, $writemembe.g. initial $readmemh("/etc/hosts", mem); / $readmemb / $writememh / $writememb | Yosys's read_verilog opens the file of a $readmemh/$readmemb itself while it reads the design (Yosys 0.53, measured: the contents become the memory's initial value; one text scores 9.331 GE with one host file and 0 GE with another; a missing file is an error). The pinned flow reads with plain read_verilog -sv (no -defer), and then Yosys opens the file in a module the top never instantiates as well, but not in a function nobody calls or in a generate branch that is not taken; the gate refuses all of them, because it reads the text. On the organizer's machine that pulls a host file (a salt, an oracle) into the netlist and leaks bits of it through the score; the proof never sees the file. Yosys 0.53 rejects $writememh/$writememb ("Can't resolve task name"), but the gate refuses them first and with a file:line:col. | never P-NO-HOST-ACCESS |
file I/O system tasks $fopen, $fclose, $fdisplay*, $fwrite*, $fstrobe*, $fmonitor*, $fscanf, $fgets, $fgetc, $fread, $fseek, $ftell, $feof, $fflush, $rewind, $ungetc, $ferrore.g. integer fd; initial begin fd = $fopen("/tmp/x", "w"); $fdisplay(fd, "hi"); $fclose(fd); end / $fscanf(fd, ...) / $fgets / $fread | Opens, reads, writes or repositions a host file (LRM 21.3). Yosys 0.53 has none of these tasks ("Can't resolve function name \$fopen'"), so without the gate they are rejected late, at the banliststage (atfourstatefor a task with no banned operators), as an ordinary rejection with Yosys's message and its line in the canonical text but no column and no reason; the gate refuses them at thesource` stage with a file:line:col and the principle, and would still refuse them under a Yosys that accepts them. | never P-NO-HOST-ACCESS |
process and environment access $system, $test$plusargs, $value$plusargs, $getenve.g. initial $system("id"); / initial if ($value$plusargs("X=%d", v)) ... / $test$plusargs("X") / $getenv("HOME") | Runs a host program or reads the host process's environment or command line (LRM 21.6; $system and $getenv are tool extensions). The checker would execute or read them on the organizer's machine. Yosys 0.53 rejects all of them ("Can't resolve task name"); the gate refuses them first. | never P-NO-HOST-ACCESS |
wave and dump writers $dumpfile, $dumpvars, $dumpon, $dumpoff, $dumpall, $dumplimit, $dumpflush, $dumpports*, $fsdb*, $shm_*, $vcdplus*, $wlf*e.g. always_comb begin $dumpfile("/tmp/x.vcd"); $dumpvars(0, band); y = a & b; end / initial begin $dumpfile("/tmp/x.vcd"); $dumpvars(0, band); end | Writes a waveform file on the host (LRM 21.7 and vendor extensions). The one family the pinned Yosys 0.53 ACCEPTS and ignores ($dumpfile, $dumpvars, $dumpon, $dumpoff, $dumpall: a warning only; a design with $dumpfile/$dumpvars scored 5.332 GE like the plain one, measured). Before the gate the form in an initial block stopped at the design stage (ls-sv has no initial, "unknown process kind Initial"; the fixture with the whole family stopped even earlier, at banlist, because Yosys rejects $dumpports and $fsdb*), but the form in an always_comb block passed every stage up to the proof (score 21.328 GE at N = 16, the same as the plain a & b): the ls-sv extractor lowers a $dumpfile(...) / $dumpvars(...) statement there to an empty statement, so ls-sv does NOT refuse these two outside initial, and the post-synthesis check cannot see them (it simulates the netlist). The other names of the family are errors in Yosys 0.53. | never P-NO-HOST-ACCESS |
DPI import "DPI-C", export "DPI-C"e.g. import "DPI-C" pure function int c_fn(input int x); / export "DPI-C" function f; | Native code (LRM 35): an imported function runs whatever the host library does, and an export lets native code call back in. Neither slang's meaning nor Yosys's (a syntax error in Yosys 0.53) is the call's; refused at the gate in the compilation unit and in a module alike. | never P-NO-HOST-ACCESS |
$sdf_annotate, $coverage_save, $coverage_merge, $set_coverage_db_name, $load_coverage_db, and the Annex D file tasks $input, $log, $nolog, $key, $nokey, $save, $restart, $incsavee.g. initial begin $sdf_annotate("/tmp/x.sdf"); $coverage_save(0, "/tmp/cov"); $input("/tmp/cmds"); $log("/tmp/out.log"); $save("/tmp/ckpt"); end | Each reads or writes a host file (an SDF file, a coverage database, a command, log, key or checkpoint file; LRM Annex D and the coverage clause). Yosys 0.53 rejects all of them; the gate refuses them first. | never P-NO-HOST-ACCESS |
escaped identifier that begins with $ (\$readmemh , \$clog2 )e.g. initial \$readmemh ("/etc/hosts", mem); / function automatic integer \$clog2 (input integer v); ... localparam integer W = \$clog2 (16); | Yosys's lexer gives the escaped identifier the very name of the system task of that spelling (measured: it runs $readmemh and reads the file), while slang reads an ordinary identifier. So even an ADMITTED system name can mean two things: a user function $clog2 returning 0 is the user's for slang (W = 0, y = 0) and the builtin for Yosys (W = 4, scored as a & b, 5.332 GE). Refused whatever the name. | never P-SAME-PROGRAM (proposed) |
| internal: the canonical text differs from the submitted token stream e.g. not triggerable by a design (a stripper bug) | Fail closed: the gate validates its own stripping with slang. | refused-now |
What you can write:
`timescale 1ns/1ps
module band #(parameter int N = 4)(input logic [N-1:0] a, b, output logic [N-1:0] y);
// comments are fine: they are stripped
assign y = a & b;
endmodule
What is refused:
`ifdef SYNTHESIS
assign y = a & b; // Yosys defines SYNTHESIS, slang does not
`else
assign y = a | b;
`endif
(* keep *) wire t; // attribute: Yosys gives it a meaning
// synopsys translate_off
initial $readmemh("/etc/hosts", mem); // host file: refused
initial $system("id"); // host program: refused
import "DPI-C" function int c_fn(int x); // native code: refused
Stage source and fourstate: no x or z may arise
ls-sv computes with unknown values like an IEEE simulator; Yosys treats them as don't-cares. Wherever an x reaches a decision the proved and the scored circuits can differ, and you choose where. Every SOURCE of an x or z is refused, exactly. A casez item may use z and ?, a casex item x, z, ?.
| refused | why | state |
|---|---|---|
| x, z or ? digits in a literal outside a casez/casex item wildcard e.g. 1'bz; 4'b10x0; 'x; 'z; 4'b1??0; 8'hz? in an assign, an == operand, a plain case item, a localparam, a `case inside` item | ls-sv computes with x/z like IEEE simulation (a z item never matches a known input); Yosys reads an x/z bit as a don't-care and drops it from comparisons: complementary items 2'b0z, 2'b1z match no input in ls-sv and EVERY input in Yosys. | later the x-taint feature: allow x sources but forbid x-tainted values from reaching non-monotone decisions; first measure the area gain of don't-cares on a real task |
| an x digit in a casez item e.g. casez (a[1:0]) 2'bx1: ... | Only z and ? are casez wildcards; an x bit in a casez item is NOT a wildcard and the two tools read it differently. | later as for x/z literals (x-taint feature) |
| a constant with unknown bits: parameter, localparam, inline constant, constant function result, out-of-range constant select e.g. localparam logic [3:0] K = 4'd1 / 4'd0; a constant function returning an unassigned local; v[6 +: 4] on [7:0]; an enum member with an x value | Constant division by zero is not banned as arithmetic but it is x: ls-sv computes with it, Yosys reads it as a don't-care. | later the x-taint feature |
| a run-time index whose type can address outside the declared range e.g. logic [N-1:0] mem [3:0]; y = mem[i] with a 3-bit i (0..7 on [3:0]); a signed index; v[i +: 4] with a 3-bit i on [7:0] | An out-of-range read is x. The rule reasons from the index TYPE alone (an unsigned w-bit index ranges 0..2^w-1): exact for the checker's purpose but it over-refuses indexes the design keeps in range by other means. Fix: declare a power-of-two range or narrow the index. | later the x-taint feature (and value-range reasoning on the index expression to narrow the over-refusal, a proposal) |
| a procedural loop counter used as an index e.g. always_comb begin for (int k = 0; k < N; k++) y[k] = a[k] & b[k]; end | Documented gap: ls-sv does not load procedural loops today, and a PR that admits them should teach this rule loop bounds. The generate-for twin passes. | later none recorded; proposed prerequisite: ls-sv loads procedural loops AND rule (d) learns loop bounds |
| a select with no fixed range or a non-constant width e.g. (not triggered: a queue reports a fixed range; slang rejects a non-constant width) | Cannot show the index stays in range. | refused-now |
| a net that is used but has no driver e.g. logic u; t = u ? 0 : 1; an unassigned output | ls-sv reads the net as x; Yosys resolves it freely (to whatever makes the logic smallest): the proved and the scored circuits differ at a point the player chooses. | later the x-taint feature |
| conflicting drivers on one net, a combinational logic loop e.g. two continuous assigns to one net; assign t = a & u; assign u = t | a; | Bus contention is not a combinational function; a loop is not a combinational function either (and the statement also needs Terminates2). | never P-SYNTHESIZABLE (proposed) |
| an inferred latch e.g. always_comb begin if (en) y = a; end (an output not assigned on every path) | A variable an always_comb block reads before it assigns it on every path holds x; Yosys proc refuses it ('Latch inferred ... from always_comb'). | later the sequential layer (tasks stay combinational), latch semantics in ls-sv and a latch price in score/ge.json |
| a design Yosys 0.53 cannot read at the scoring point, or any slang error there e.g. a syntax error; a function with `return`; a queue declaration; GenvarUnknownBits | Fail closed: a design Yosys cannot read cannot be scored. Yosys 0.53 is narrower than slang. | refused-now |
an error of slang's analysis pass (two writers of one variable, mixed assignments, a written input, an always without timing control)e.g. always_comb cout = c[N]; always_comb cout = c[N]; / assign cout = c[N]; always_comb cout = c[N]; / assign cin = a[0]; / initial cout = 0; always_comb cout = c[N]; / always cout = c[N]; | A program slang's driver rejects is rejected (decided 2026-11-01, analysis errors included). Yosys accepts several of them silently and the Lean loader refused them only by accident or not at all (initial plus always_comb: the loader has no Initial process kind). | never P-SAME-PROGRAM |
What you can write:
always_comb begin
casez (a[1:0])
2'b1?: y = 1'b1; // wildcard digit in a casez item: allowed
default: y = 1'b0;
endcase
end
What is refused:
assign y = a[0] ? 1'bz : a[1]; // z literal
localparam logic [1:0] K = 2'd1 / 2'd0; // constant division by zero is x
logic [3:0] m; ... m[{1'b1, a[1:0]}] // index can leave the range: x
logic u; assign y = u ? a : b; // u is undriven
Stage banlist: the task's banned operators
A task bans the run-time operator that IS the task, plus whatever Yosys merges into the same cell (alumacc turns + and - chains into the cells of *). Constant arithmetic (parameters, genvar loops, index arithmetic over parameters and genvars) and comparisons stay allowed. Which operators are banned is per task: add, mul and _example ban + - * / % **; fdivp bans / % **.
| refused | why | state |
|---|---|---|
a run-time + - * / % ** in a task that bans ite.g. assign y = a + b; a - b; a * b; a / b; a % b; a ** b; with run-time operands | The operator that IS the task, plus what Yosys merges into the same cell (alumacc: + and - chains become the cells of *). Constant arithmetic stays allowed. Which operators: tasks/*/task.json banlist.operators. | refused-now |
| the same arithmetic in other forms: compound assignment, unary minus, ++ / --, array .sum() / .product(), $countones & co, run-time index arithmetic e.g. y += a; -a; x++; arr.sum(); $countones(a); a[j + 1]; x[sel*W +: W] | Each builds the same $add / $sub / $mul / $neg cell. Procedural for-loop counters (k++) and constant arguments stay allowed. Yosys 0.53 cannot read .sum() at all, a later one may. | refused-now |
| a banned operator hidden behind a constant-argument function call, in a generate branch the scoring point does not take, or behind a loop counter that is written e.g. function automatic logic [15:0] g(input int k); g = a / 16; endfunction assign q = g(0); for (int k...) begin k = int'(a[0]); ... end | A call is constant only when its arguments are AND its body reads nothing but its own arguments and locals; Yosys silently ignores a write to a loop counter and folds the loop, simulation does not (the proof and the score then differ). | refused-now |
| a design Yosys or slang cannot read at a backstop point e.g. syntax Yosys 0.53 lacks (arr.sum()); an undeclared identifier | A design Yosys cannot read cannot be scored; the front check cannot walk what slang rejects. The banlist fails closed. | refused-now |
What you can write:
for (genvar i = 0; i < N; i++) begin : g
assign s[i] = a[i] ^ b[i] ^ c[i]; // gates, not +
assign c[i+1] = (a[i] & b[i]) | (c[i] & (a[i] ^ b[i])); // i+1 is constant arithmetic
end
What is refused:
assign s = a + b + cin; // run-time + in a task that bans it
assign y = a * b; // run-time *
assign n = $countones(a); // Yosys builds it from $add cells
assign p = arr.sum(); // array reduction = run-time +
Stages extract and design: the language tier and the parameter list
The proof is about what the pinned extractor and the ls-sv loader accept. A construct they refuse fails here with the tier's own message. The top module's overridable parameters must be exactly the task's, by name and order, because the scoring script overrides them by name.
| refused | why | state |
|---|---|---|
| the extractor refused the design e.g. anything the pinned extractor refuses: a missing top module, a parameter the probe points name that the top lacks, any Unsupported construct | The proof can only be about what the extractor reads. A pass-through for the language tier's refusals (docs/sv-refusals.md). | refused-now |
| an envelope that loads but contains an Unsupported process or expression e.g. a user function call in an always_comb or in an assign (`assign y = both(a, b)`), a procedural loop, any construct the extractor embeds as an `Unsupported` node instead of refusing | The extractor exits 0 and load_design_sv2 builds design.sem even when a construct is unsupported: the node is kept as a loud Process2.unsupported/Unsupported expression and refuses only when run. No proof about such a design can hold, so the design stage rejects it before any proof, scoring or leaderboard step, naming the first loud node, at the scoring point and at every probe point not larger than it. | refused-now |
| the top's overridable parameters differ from the task's e.g. an extra `parameter int M = 2` (localparams are free) | The scoring script overrides the parameter by name (-chparam N 16); another name or an extra parameter would score a different circuit than the one proved. | refused-now |
| the design does not load (ls-sv refused it) e.g. loader refusals: cross-checks, the one-driver rule, AlwaysComb:self-read / alias-read, select guards, the probe cross-check; a parameter declared narrower than int | Pass-through for the loader's refusals; the cross-check at the scoring point keeps the scored design one ls-sv and slang agree on. | refused-now |
What you can write:
module band #(parameter int N = 4)(input logic [N-1:0] a, b, output logic [N-1:0] y);
What is refused:
module band #(parameter int N = 4, parameter int M = 2)(...); // an extra parameter
module band #(parameter bit [3:0] N = 4)(...); // narrower than int: N = 16 reads as 0
Stage imports: the proof file
The checker builds only SvGolf, the task modules and the evaluator; a module outside the allowlist is unaudited code outside the trusted base. Mathlib and its packages (Batteries, Aesop, Qq, ProofWidgets, Plausible, ImportGraph, LeanSearchClient) are on the allowlist. One solution file in v0.
| refused | why | state |
|---|---|---|
| imports outside the allowlist e.g. import LeanModels.Python.Semantics; import Examples.python.tri.proof; import Mathlib2.Tactic; import Lake | The checker builds only SvGolf, tasks and svgolf-eval, so a module outside the allowlist is code nobody audited and stays outside the trusted base. Mathlib and the packages its import closure reaches (Batteries, Aesop, Qq, ProofWidgets, Plausible, ImportGraph, LeanSearchClient: the module roots, pinned by lean-surfaces' lake-manifest.json) ARE admitted since 2026-10-31: a proof that uses them is accepted on the same terms as any other, by the kernel from the task's allowed axioms alone. The comparator walks the dependency closure for axioms (a native_decide behind a Mathlib lemma is rejected like one in the file) and REPLAYS that closure, minus the toolchain and the submission's own modules, through the kernel, so a package olean whose declaration was added without kernel checking is rejected; toolchain modules must resolve into the toolchain; the integrity tripwire watches the Lean search path (.olean files of the packages, entries that do not exist) for every solution; the imports Lean recorded in the compiled solution are compared with the header the checker read. | never P-TRUSTED-BASE (proposed) |
| a second solution file e.g. import MyHelpers | One file in v0: there is no module the checker could stage or hash for a second file. | later none recorded; proposed prerequisite: multi-file staging, hashing and an integrity snapshot of every player file |
the solution must import Submission.Designe.g. a Solution.lean without that import | The theorem must be about the design the checker extracted. | never P-SAME-PROGRAM (proposed) |
module or prelude headers; a dangling importe.g. a Lean file starting with `module` or `prelude` | The header parser (comment stripper plus token scan) only understands ordinary import headers. | refused-now |
What you can write:
import Submission.Design
import LeanModels.Sv.Ingest2
import SvGolf.Comb
import Mathlib.Tactic.Ring
What is refused:
import Examples.python.tri.proof
import LeanModels.Python.Semantics
import Mathlib2.Tactic -- a lookalike
import MyHelpers
The trusted statement: interface, clocks, termination, function, range
The statement is a Lean Prop fixed by the task (SvGolf/Task.lean). A design that satisfies it must have exactly the task's ports, no edge-phase process, a completed run for every schedule and stimulus, and the task's function at every cycle, for every N in the range. These are not rules you can argue with: a design that breaks one has no proof of the statement, and the repository checks the refutations in tests/*/negative/Refute.lean.
| refused | why | state |
|---|---|---|
| ports that are not EXACTLY the task's (names, order, widths) e.g. an extra or hidden input (en, a rounding-mode pin); a renamed port (ci for cin); a wider or merged output; an inout | Stimulus conventions ignore unknown names and store values at their own width, so only pinned ports mean what the hypothesis says. | refused-now |
| any edge-phase process (always_ff, always @(posedge clk), an async-reset block), even with a dead clock e.g. always_ff @(posedge clk) s <= a ^ b; clock tied low or undriven | ls-sv runs every edge-phase process once per cycle whatever its clock holds, so a register on a clock nothing drives holds the right value in ls-sv and x in every simulator (0 GE in Yosys). | later the sequential layer: ls-sv models clock edges; the Unclocked requirement goes then |
| a design that never settles or has no completed run (Terminates2) e.g. a combinational loop; a design the loader refused (errorDesign) | Models2 quantifies over completed runs only, so a design that never settles satisfies every property vacuously; Terminates2 requires a completed run for every schedule and stimulus. | never P-DETERMINISM (proposed) |
| outputs that are not exactly the task's function at EVERY cycle with fully known inputs e.g. the wrong function; an output left x/z; correct only at the scoring point; outputs depending on earlier cycles | The conjunct holds at every cycle of every run, whatever the earlier stimulus; a design cannot keep the hypothesis false by driving its own inputs. | refused-now |
| a clocked transaction that misses the frozen TxTask guarantee: done not within lat cycles of the start edge, or done and the payload not held at result x until the next accepted start or reset e.g. an off-by-one step counter; unsigned instead of signed products; a missing sign extension; done one cycle early; the accumulator not cleared on start; an accumulator truncated to 2W | TxTask.Statement (txtask-v1, svgolf/SvGolf/TaskSeq.lean) claims Guar for every transaction under Released and Rely, at every parameter point in range, and the proof must establish it; a design wrong at any point, or late by a cycle, fails at the solution stage. Each matmul attack is also wrong at post-synthesis (check.py --unproved), so none is merely different. | refused-now |
| a clocked stream that misses the frozen StreamTask guarantee: out_valid unknown or raised before the first start, or a transaction's out_valid cycles not emitting exactly spec xs (the input sorted ascending, duplicates kept) within lat cycles of the start edge e.g. the token shift reading the cell's own token (no value ever emitted); a missing else (two writes, the later wins); the neighbour push comparing the cell's own value; the valid token entering with the start (out_valid one cycle early, one value too many); a reset that leaves the cells; a top cell the drain does not clear; signed instead of unsigned order | StreamTask.Statement (txtask-v1, svgolf/SvGolf/TaskSeq.lean) claims Guar for every transaction under Released and Rely, at every parameter point in range, and the proof must establish it; a design wrong at any point, or one value early or late, fails at the solution stage. Each sorter attack is also wrong at post-synthesis (check.py --unproved), so none is merely different; designs that differ from the reference only in how they order EQUAL values (>= for >) are not attacks, since equal values are interchangeable in the emitted list. | refused-now |
| a proof that holds at the scoring point N = 16 only e.g. a ripple adder at N = 16, carry-free elsewhere | The statement quantifies over every N in the task's range (1 <= N), so a one-width proof does not typecheck; a fixed-size task (aes_sbox) is StatementFixed. | refused-now |
| a task with two or more overridable parameters e.g. (not expressible: CombTask.sem takes one Nat) | The statement type is one-parameter. | later none recorded; proposed prerequisite: a multi-parameter CombTask (sem over a tuple) and its Comb.lean reduction |
What is refused:
module adder(input logic [N-1:0] a, b, input logic ci, ...); // port named ci, not cin
output logic [N:0] s; // merged {cout, sum}
always_ff @(posedge clk) s <= a ^ b; // any edge process
Stages comparator, leanchecker, integrity: the proof itself
The kernel must accept your theorem from the task's allowed axioms alone. Everything that would let a proof depend on something other than the kernel is refused.
| refused | why | state |
|---|---|---|
no theorem Submission.solutione.g. a Solution.lean that defines no such theorem, or defines it with `def` | The comparator asks the kernel to accept theorem _ : Submission.Goal := Submission.solution. | refused-now |
| a proof of another statement e.g. theorem solution : True := trivial | The accepted theorem must be exactly the generated Goal, checked by the kernel against the solution's type and proof term. | never P-TRUSTED-BASE (proposed) |
sorry and admite.g. theorem t : ... := by sorry; also a goal ADMITTED by a tactic after it logged an error (the WP-G tactic elaborators log their refusal and admit the goal, so the proof compiles with an error and depends on sorryAx) | The proof depends on sorryAx, which the allow list excludes. | never P-TRUSTED-BASE (proposed) |
native_decidee.g. theorem t : ... := by native_decide | native_decide trusts the compiled evaluator, not the kernel: it adds a per-declaration axiom <decl>._native.native_decide.ax_1_1 that no static allow list can name. | refused-now |
bv_decide (and allow_bv_decide: true in a task)e.g. theorem t : ... := by bv_decide; task.json allow_bv_decide: true | bv_decide runs an external SAT solver (the toolchain's cadical), and the checker does not support external solvers (decided 2026-11-01: "Let's not support external solvers for now"). Supporting it needs the solution's compile sandbox to grant fork, the exec of cadical and a writable temporary directory (measured: all three), which also lets a hostile file start further copies of lean. Without the grant the tactic fails in the sandbox with operation not permitted (check.py adds a hint at solution); with --sandbox off the comparator rejects the generated axiom <decl>._native.bv_decide.ax_*, naming the policy; allow_bv_decide: true is an infrastructure error. The comparator's re-verification of the axiom and the sandbox grant are kept, dormant, behind the constant BV_DECIDE_SUPPORTED in check.py. | refused-now |
| a hand-declared axiom e.g. axiom cheat : Goal | Only the task's allowed_axioms may appear in the solution's dependency closure, walked through kernel values (not the #print axioms cache). | never P-TRUSTED-BASE (proposed) |
extern / implemented_by on submission constantse.g. @[implemented_by fastId] def slowId | They only matter for compiled code, which native evaluation can reach: the compiled meaning would differ from the kernel meaning the statement is about. | never P-TRUSTED-BASE |
| declarations added with the kernel check switched off e.g. Environment.addDeclCore ... (doCheck := false) for the top theorem or for a helper lemma | A proof must be replayable by the kernel from scratch (the comparator catches the top theorem, leanchecker the helpers). | never P-TRUSTED-BASE (proposed) |
| a solution whose build changes a trusted file e.g. tactics or #eval that change the staging tree, score/*.py, check/*, svgolf-eval, simcells.v, the svGolf / LeanModels.Sv builds, the toolchain, Yosys, Verilator, the C++ compiler | A tripwire, not a sandbox (an escaping process or tamper-and-restore is beyond it). A concurrent rebuild of LeanModels.Sv also trips it. | never P-TRUSTED-BASE (proposed) |
| the proof or the replay overruns its time limit e.g. Solution.lean slower than task.json time_limit_s; comparator or leanchecker slower than verify_time_limit_s | Bounded checking time; the limit excludes waiting for the Lean slot wrapper. | refused-now |
What you can write:
theorem solution : Submission.Goal := by
-- induction on bit indices, decide +kernel for fixed-size subgoals
...
What is refused:
theorem solution : Submission.Goal := by native_decide -- trusts the compiler
theorem solution : Submission.Goal := by sorry
axiom cheat : Submission.Goal
Stages score and postsynth: the scored netlist
The score is area in NAND2 gate equivalents and depth, measured on the pinned Yosys + ABC netlist. The post-synthesis check then simulates ls-sv and the scored netlist on the task's vector budget and rejects any difference; it is sampling, so a difference confined to a tiny input region can slip through, which is what the static rules above are for. A rejection here is a bug report: either ls-sv or Yosys is wrong.
| refused | why | state |
|---|---|---|
| a netlist with cells the GE table does not price e.g. always_ff @(posedge clk) y <= a & b; (a $_DFF_P_ survives into the netlist) | A combinational task's netlist contains only AND/NAND/OR/NOR/XOR/XNOR/ANDNOT/ORNOT/MUX/NOT; latches and flip-flops are not scored. | later the sequential layer and a flip-flop / latch price in score/ge.json |
| Yosys cannot synthesize the design; the gate refused it; non-deterministic synthesis; depth over the task's budget e.g. a syntax error; score.py without --no-gate on a refused file; --repeat K with differing results | No netlist, no score; a score must be reproducible. The depth budget is live but unexercised (every current task sets depth_budget null). | refused-now |
| ls-sv and the scored netlist differ on a vector e.g. any vector of the budget on which design.sem (run2) and the Verilator netlist model differ | The proof is about ls-sv's reading, the score about Yosys's: the backstop for disagreements nobody has found. It is sampling: a difference confined to a rare region can pass. A rejection is a bug report. | refused-now |
| latent x sources ls-sv does not support yet: a function local read before assignment, a never-loaded always_latch, always @* with a path that assigns nothing e.g. tests/_example/attacks/fn_uninit.sv (rejected at `design`; the postsynth check alone rejects it too), latch_never.sv and latch_star.sv (rejected at `fourstate` since WP-PROC follow-up 3; before it at `design`) | x in IEEE, resolved freely by Yosys (to 1 or 0). ls-sv has no user function calls, no AlwaysLatch, no always @*, so no static rule sees it. Since WP-C3 the design stage rejects the loaded envelope that carries the Unsupported node (Design2.hasUnsupported); the postsynth stage, alone, still fails closed on it. Both lines hold today. Since WP-PROC follow-up 3 (2026-11-01) the latch half is also closed at the original text: rule (e) of score/fourstate.py refuses any latch proc infers (select -assert-none t:$dlatch t:$adlatch t:$dlatchsr), whatever ls-sv models, and fourstate runs before design. | later for a function local read before assignment: a 4-state rule in score/fourstate.py MUST land BEFORE ls-sv models function calls; the latch half is done (fourstate.py rule (e)), a latch construct ls-sv models would still need a rule for its initial x (plan LATCH4) |
| fail-closed postsynth conditions: x/z in an ls-sv output, an unknown-valued constant in the netlist, a netlist Verilator will not lint / build / run, σ_src ≠ σ_rev, port sets that differ, a netlist that does not re-read, an inout port, a non-plain identifier e.g. a tied-low clock leaves a 16'hx constant in the netlist and rejects every vector | Verilator is 2-state and would silently turn an unknown constant into 0 or 1; a result that depends on the schedule is not a combinational function; everything unclear fails closed. | refused-now |
What is refused:
always_ff @(posedge clk) y <= a & b; // a flip-flop in the netlist: not priced
Task authors: task.json
Mistakes in a task definition fail loudly so a typo cannot silently change the vector budget or the banlist.
| refused | why | state |
|---|---|---|
| task.json banlist naming an operator outside + - * / % ** e.g. "banlist": {"operators": ["<<"]} | Only the six arithmetic operators have a Yosys cell map and a slang operator map. The CLI banlist.py --ban '<<' dies with a KeyError traceback instead of the clean message. | later none recorded; proposed prerequisite: a task that needs it (barrel shifter bans << >> <<< >>>; a comparator bans < > <= >=), then OP_CELLS and _binop_symbol extended with $shl $shr $sshl $sshr $lt $le $gt $ge |
| task.json with unknown keys, bad postsynth corners, a status other than ready/draft e.g. a typo in a postsynth key; corners naming a non-input port; a ready task without a proved reference | Schema discipline: typos must not silently change the vector budget. | refused-now |
What is refused:
"banlist": {"operators": ["<<"]} // unknown operator
"allow_bv_decide": true // an infrastructure error: no external solvers
Constructs the ls-sv tier refuses, as seen from svGolf
The extract and design stages pass on the refusals of the language tier (ls-sv). The ones a task is most likely to run into, with what has to exist first:
| construct | why you might want it | prerequisite |
|---|---|---|
ForLoopStatement | popcount, carry chains and parity trees written as loops (task ladder: popcount) | remaining prerequisite: broader loop state for effectful or nonliteral bounds |
CallExpression (user function call in expression position) | helper functions for bit tricks, parity and constant functions | remaining prerequisite: a 4-state rule (score/fourstate.py) for unassigned or multiply-written locals and general call-frame semantics |
LocalDecl (scoped block-local declaration) | block-local temporaries in always_comb (static locals of a module-level always_comb are hoisted since WP-PROC L1; the remaining scoped forms load and retain a named LocalDecl refusal) | none for a static block local of a module-level always_comb (hoisted, WP-PROC L1), including closed literal/fill/parameter/enum initializers; proposed prerequisite for the rest (runtime/effectful initializers, automatic variables, generate scopes): a LocalDecl statement (scoped temporaries) in Stmt2 / PStmt |
CallExpression ($countones, $onehot, $left, array and enum methods) | $countones and one-hot predicates for fixed-width packed signals (the banlist already treats these bit queries as +) | none for fixed-width $countones, $onehot, $onehot0, and numeric enum methods; proposed prerequisite: per-built-in lowering for parameter-dependent widths, $left, enum name(), and array methods |
ConditionalStatement:Unique | :Unique0 | :Priority | unique / priority in priority encoders and one-hot muxes | WP5: a violated unique / unique0 / priority claim becomes a run-time error on the settled state, forced by Terminates2 |
any edge-phase process (always_ff, always @(posedge clk), an async-reset block), even with a dead clock | registered, pipelined and multi-cycle designs (serial multiplier, iterative divider) | the sequential layer: ls-sv models clock edges; the Unclocked requirement goes then |
ConversionExpression:Explicit | size casts such as 8'(a + b) are common in golf code | per-point width and signedness rules for parameter-named and aggregate casts |
PortSymbol:2state | VariableSymbol:2state | NetSymbol:2state | NamedValueExpression:2state | int / bit declared types for loop variables and flags | 2-state value semantics for remaining net/integer/aggregate declarations and width-aware boundary witnesses |
PortSymbol:signed | VariableSymbol:signed | NetSymbol:signed | signed arithmetic and comparisons | none for fixed signed array variables and ports; parameter-dependent signed aggregate storage and exact width-changing signed conversions in Param2 / Sem2 remain to be defined |
SimpleAssignmentPatternExpression | StructuredAssignmentPatternExpression | ReplicatedAssignmentPatternExpression | lookup tables and constants written as '{...} patterns | symbolic assignment patterns for parameter-dependent, unpacked and effectful aggregate shapes |
InsideExpression, ValueRangeExpression | range checks (a inside {[3:5]}) in decoders | point-dependent membership widths and a four-state simulator oracle for the remaining symbolic forms |
instance hierarchy deeper than its module library (recursive instantiation) | recursive instantiation: bitonic sort, Karatsuba (F3b) | flattening by induction in Hier2 / Hier2Gen with a symbolic recursion depth (bitonic sort, Karatsuba) |
PrimitiveInstanceSymbol:PrimitiveInstance | built-in gate primitives (and g1(y, a, b)) for gate-level golf | none recorded; proposed prerequisite: built-in gate primitives first; UDP tables have no recorded plan |
x, z or ? digits in a literal outside a casez/casex item wildcard | don't-care assignments ('x) to save area, via the x-taint feature | the x-taint feature: allow x sources but forbid x-tainted values from reaching non-monotone decisions; first measure the area gain of don't-cares on a real task |
AssignmentExpression:compound | y += a and friends in procedural code | none recorded for the lifted subset; the residue is the effectful and nonblocking forms |
And what will not be accepted for as long as the tools disagree or the construct is not hardware:
- Anything the PIPELINE tools read differently (slang and ls-sv for the proof, Yosys for the score, Verilator and Xcelium as simulators;
P-NO-SILENT-PICK, andP-SAME-PROGRAMwhere Yosys differs from slang): narrow net actuals into wider net inputs, partly out-of-range part selects, bit-select indexes wider than 32 bits, context-width port actuals, instance-parameter overrides evaluated at different widths, initialized variables with a second writer, the hex string escape\xHH(Yosys and Xcelium read the letter x; the named control escapes\a \v \fare admitted since 2026-11-01, Xcelium alone differs), self-readingalways_comb(the event-driven simulators against the synthesizers). A difference that only Icarus (or only Xcelium, against LRM text the others follow) shows is a declared departure, not a refusal. - Programs slang rejects (
P-SAME-PROGRAM; what the slang driver reports: parse and elaboration errors always, analysis errors too since 2026-11-01 (the banlist front check and the fourstate stage read them), with what is still unread listed under 'limits'): undeclared names, two writers of one bit when one of them isalways_comb/always_ff, writing an input port, an undefined module, analwayswithout timing control. Yosys alone would accept several of them silently. - Tool directives of any kind (
P-NO-TOOL-DIRECTIVES): `ifdef SYNTHESIS, attributes, synthesis-pragma comments. - Delays, timing and randomness in a combinational task (
P-NO-TIME,P-DETERMINISM); non-hardware constructs such as classes, strings, reals, dynamic memory andfork/join(P-SYNTHESIZABLE: out of scope while the scored frontend is Yosysread_verilog -sv). Host access (reading files, system calls, DPI) is disallowed by its own policy (decided 2026-10-31). - Anything that lets a submission change what the checker trusts (
P-TRUSTED-BASE):sorry, user axioms,extern/implemented_by, a player-supplied envelope. - Anything in the SystemVerilog text that reaches the machine the checker runs on (
P-NO-HOST-ACCESS; the Lean file is not covered, see the limits):$readmemh/$readmemb/$writemem*,$fopenand the file I/O family,$system,$test$plusargs/$value$plusargs, dump writers ($dumpfile, ...), DPI imports and exports, and escaped$-names that Yosys reads as system tasks.
How a refusal becomes allowed
- Every refusal above has a state.
refused-nowmeans no change is planned.laternames the prerequisite that has to exist first; when it exists, the refusal is lifted by a pull request. - How to propose it. Open a pull request against lean-surfaces that adds the feature to ls-sv (extractor,
LeanModels/Sv), with differential cases against independent oracles (Xcelium pinned data, Icarus, Verilator, Yosys): a disagreement is fixed or declared, never ignored. The maintainers approve every such PR. - If the construct brings an x source (a function local read before assignment, a latch that is never loaded,
always @*), a rule inscore/fourstate.pymust land first or in the same PR; the post-synthesis check is the backstop, not the plan. - For the checker's own rules (the source gate, the banlist, the statement), changing a refusal changes what every earlier submission means: the PR must say so, and leaderboard entries record the lean-surfaces revision they were checked against.
- Update the register. Add or edit the row in
docs/sv-refusals.json, runpython3 tools/gen_sv_refusals.py --write, and--checkmust pass.
Known limits (not refusals)
- The post-synthesis check is sampling (fixed corners, task corners, harvested comparison constants, seeded random): a difference confined to a region no comparison constant selects can pass (
oob_rare.sv,zpair_add_rare.svpass it alone; the static rules are the only line there). - There is no sandbox:
import Leanis allowed, so a Solution.lean can run#evalwith checker privileges. The integrity snapshot is a tripwire, not a defence. Run untrusted submissions in a throwaway container. Measured (2026-10-31): a reference Solution.lean with an added#eval IO.FS.readFile "/etc/hosts"and anIO.FS.writeFilewas ACCEPTED and did both, so the host-access refusals (P-NO-HOST-ACCESS) are a rule about the SystemVerilog text only. - Open: should the Lean file be gated against host access too (
#eval,IO,run_cmd,elab,initialize,unsafe;import Leanis allowed)? Nothing gates it today. - The parameter's declared type is not pinned by the statement; the multi-point cross-check at the scoring point keeps the scored design one ls-sv and slang agree on, elsewhere the statement is about ls-sv's reading.
- The banlist front check is heuristic (a variable copied from a loop counter counts as run-time); the Yosys backstop covers the scored points only.
- The fourstate stage checks the SCORING POINT only (not the probe points) and filters
tri-statewarnings out of Yosyscheck. Its run-time index rule reasons from index TYPES:a[i-1]with a 2-bitiis a 32-bit index and is refused on any declared range (over-refusal), as is any procedural loop counter used as an index. - A gate hole not yet measured: x/z digits are exempt anywhere inside a casez/casex item EXPRESSION, not only as the item's own literal (
casez (a[1:0]) (a[0] == 1'bz) ? 2'b00 : 2'b01: ...passes the gate). Whether ls-sv and Yosys then differ was not measured; do not rely on it. - A plain syntax error is reported by the gate as
preprocessor-skipped textbefore the extractor or Yosys see it;always @(*)passes the gate (the(*)is exempt) and is refused later by the language tier. ARITH_PRIMITIVES($alu $macc $macc_v2 $lcu $fa) in the banlist backstop are dead entries: the backstop reads without-icells, so instantiating one fails earlier withyosys failed.--no-gateinscore.pyandpostsynth.pyis a research flag: the result saysgated: falseand is not a svGolf score. Missing Verilator, make, a C++ compiler, Yosys orsvgolf-evalis an infrastructure error (exit 2), never a rejection.- Open policy question: comparisons are not banned in
add/mul; a carry written as a comparison is Yosys's own alu logic and scores lower depth than the proved ripple reference. - Open: must challenge proofs be parametric (for all N)? The README treats it as decided;
svgolf/PLAN.mdstill lists it as a question for the maintainers. - slang's ANALYSIS-pass errors (
multiple-always-assigns,mixed-var-assigns,multiple-cont-assigns, "cannot assign to input port", "'always' procedure does not advance time") are read since 2026-11-01 by the banlist front check and thefourstatestage (score/slangdriver.py, the Driver's own severities, per parameter point) and by the extractor. Still unread: the elaboration warnings the driver promotes to errors (IndexOOB,RangeOOB, ...:fourstate's rule (c) refuses the out-of-range ones as x sources); a task with an EMPTY banlist skips the banlist front check, sofourstateis the only stage that reads them there; a module the design does not instantiate (docs/sv-refusals.json,holes).
Where to read more
Paths in the proglib repository:
svgolf/README.md: rules, checker stages, scoring,task.jsonfields, checker v0 limitssvgolf/PLAN.md: decisions with dates, the task ladder, the day-by-day plansvgolf/SvGolf/Task.lean: the trusted statement and why each conjunct existssvgolf/tests/expected.json: every attack and negative design, its stage and whysvgolf/tests/_example/attacks/README.md: the attacks, one paragraph eachsvgolf/design/nondeterminism/NOTE.md: design note: how LRM-legal nondeterminism would be modelled, and what the golfer would provedocs/sv-refusals.md: (lean-surfaces) the register of the language tier's refusalsdocs/sv-oracle-disagreements.md: (lean-surfaces) where the oracles split and what is refused or declareddocs/sv-xcelium-audit.md: (lean-surfaces) the Xcelium reference audit behind the round-6 refusalsdocs/sv-envelope-schema.md: (lean-surfaces) the envelope contract and the Unsupported vocabularydocs/sv-tools-split-audit.md: (lean-surfaces) the tools-split audit behind the 2026-10-31 category changes