Skip to content

v1.21.1

Choose a tag to compare

@martinholovsky martinholovsky released this 05 Aug 07:20
· 8 commits to main since this release
51e7737

[1.21.1] - 2026-08-05

The inert-control release — three days of "the rule was there, the probe wasn't."
An external audit spec (seven classes beyond our five) and two commissioned research
reports were taken in, and the recurring finding was the same each time: the library
stated the BUILD rule and had never written the AUDIT probe. It said "unit tests
touch no sockets" in three places and offered no way to find out that they do; it
governed the numbers a tool prints and not the words; it declared instruments to be
controls without ever turning that recursion on guards. The inert-control family is
now three files — rules/10 catalogs, rules/11 sweeps, rules/12 proves a control
works and distrusts whatever did the proving.

Front door checked: negative control · metamorphic · oracle problem · rules/12

Nothing here is measured; no lift is claimed for any of it. The AUDIT arm remains
at +0.00 across seven instruments, and none of this changes that.

Naming the prior art, and the four gaps two research reports found. Two
commissioned reports on inert controls were evaluated against the tree. They agree
on the headline — the missing layer is a control shown capable of failing — and
they are not of equal quality: one misquotes NIST SSDF PO.3.3, over-generalises the
formal-verification vacuity statistic from hardware to software, and sources
aviation self-test to Scribd uploads. Every claim that became library text was
checked against a primary source first, which is why four gaps landed out of
thirteen proposals. Full verdicts, both rejections, and the misquote are in
docs/ADOPTION-LOG.md (entry 2026-08-05).

Nothing here is measured; no lift is claimed for any of it.

Added

  • rules/12 §3 — per-target kill verification. A guard protects a
    population. Watching it reject one member proves the predicate can fire and
    says nothing about the other 19; the real shape is a tripwire that fired for 2
    of 20 targets and stayed green for 18, indistinguishable from full coverage on
    any single-instance test. Inject the defect into each member. For a security
    gate the bar is a 100% kill rate — unlike a code mutation score, where
    surviving mutants are triaged and a number below 1.0 is normal.
  • rules/12 §2.4 — evidence the subject supplies about itself. An instrument
    that accepts the evaluated party's own report is transcribing, not measuring.
    The anchor is now measured: across 1.5M assets and 128K agents, "over 84% of
    approved assets bypass quality checks using vacuous tests (e.g. console.log())"

    (arXiv:2605.25815) — the platform accepted
    each agent's own execution log as proof. Notably the other report asserts no
    such corpus exists; this one found it.
  • rules/11 §2.6 — when you cannot state the right answer, state how it must
    change.
    A metamorphic relation as a liveness oracle for a tool: commit a
    fixture with N known items, assert the count is N, assert it rises when you add
    one. The only diagnostic in that file that catches an analyser emitting an
    empty-but-well-formed artifact while exiting 0.
  • sota-devsecops rules/05 §5.6 — what the standards ask for, and the one thing
    none of them ask.
    SSDF PW.8.2/PO.3.3 and CRA Annex VII require a
    record that the scan ran; OpenSSF Scorecard's SAST check detects tool
    presence only, and its Dependency-Update-Tool check says outright it "does not
    ensure that the tool is run". None require evidence the gate can fail — SLSA
    will sign provenance for a scanner configured to scan zero files. So a passing
    compliance check is evidence of process, not protection, and the negative
    control has to be a house rule.

Changed

  • The cross-discipline lineage is now named. The library had described proof
    tests (IEC 61508's dangerous-undetected framing), positive controls, aviation
    built-in test, poka-yoke, vacuous satisfaction (Ball & Kupferman, quoting
    Beer et al. on hardware verification) and the test oracle problem (Barr,
    Harman, McMinn, Shahbaz & Yoo, IEEE TSE 41(5):507–525, 2015) for months
    without using any of those words — poka-yoke and oracle problem returned
    zero hits across all 41 skills. Named in rules/12 intro + §3 and
    rules/11 §2.6.

Rejected (recorded so they are not re-litigated)

  • A minimum mutation-score threshold in CI — contrary to sota-testing
    rules/07 §7.2 ("never set a global percentage target") and rules/06 §6.3
    differential mutation, adopted 2026-07-24. The per-gate kill rate above is
    compatible and was adopted; a global score is not.
  • GSN / assurance-case notation — a notation, not a mechanism, and its own
    cited critique (arguments that "assume the conclusion") describes what
    sota/rules/01 §7 adversarial refutation already prevents.

Not verified, and therefore not asserted

IEC 61508 §3.8.5/§3.8.6 clause text is paywalled — the concept is named and no
clause number is quoted. The CRA Annex VII point number was not confirmed
(EUR-Lex returned only recitals); the sentence was verified against published
copies of the regulation, and rules/05 says so at the point of use.

The rules/12 split — the inert-control family gets a third file. The previous
entry below shipped rules/10 and rules/11 at 493 and 495 of the 500-line cap and
said plainly that the next addition needed a split rather than another squeeze. This
is that split, and the boundary is not arbitrary: rules/10 catalogs inert controls,
rules/11 sweeps for them at scale, and rules/12 is the third move — proving a
specific control works, then turning the same suspicion on everything that did the
proving.
That layer was previously split across two files (a mutation procedure at
the end of one, an instrument section at the end of the other) and belonged together.

Added

  • sota-code-security rules/12 — Verifying the Verifier (191 lines). Composed
    from rules/10 §3 (the mutation probe for a security control) and rules/11 §7
    (your instrument is a control), plus the guard that is an instance of what it
    guards
    promoted from a bullet to its own §3 with all three forms written out:
    the predicate the defect satisfies ("auth=" in line passes on auth=None), the
    guard nested in another gate's success branch, and the denominator counting only
    survivors. Its organising asymmetry: a broken feature produces a complaint, but a
    broken verifier produces a green tick or a number, and both get believed.
  • rules/11 §7 "Then turn the lens around" — a short hand-off replacing the
    moved section, stating the rule that motivates the file: a finding produced by an
    unvalidated instrument is not yet a finding.

Changed

  • rules/10 renumbered: §3 (vacuous tests) moved out, so §4 → §3 (degradation
    helper) and §5 → §4 (evidence rules). All live cross-references repointed —
    rules/11 ×4, sota-devsecops rules/03, README, docs/CONVENTIONS-LEDGER.md,
    evals/README.md ×2, and a comment in scripts/check-invariants.sh. Historical
    CHANGELOG and ADOPTION-LOG entries were deliberately not rewritten: they
    record what shipped at the version they describe.
  • ### 7.1 Four failure modes listed five bullets — count-rot introduced by the
    previous entry when the guard bullet was added without updating the heading. Fixed
    structurally rather than by editing the number: the guard is now its own section
    and 7.1 (rules/12 §2.1) is back to four.
  • The router's library map had never listed rules/11. skills/sota/SKILL.md
    stopped at "10 silent control failure"; it now lists 10, 11 and 12. Invariant 7
    gates skills against the router, not rules files, so this drifted unnoticed —
    and sota/SKILL.md is itself at exactly 500 lines, so the entry was rewritten to
    fit the same four lines rather than growing the file.
  • rules/11 §2.2 — the runners disagree, which is the point. Both verified by
    running them: go test ./... over a package with no test files exits 0, while
    pytest on the same empty scope exits 5 — as it does for a file with no test
    functions and for a -k selector that deselects everything (pytest 9.1.1). One
    fails closed, one fails green, so no folklore about "runners exit 0 when they find
    nothing" tells you which you have. (The previous entry deliberately omitted the
    pytest half as unverified; it is now measured.)
  • README file count 297 → 298 (invariant 6 again), and the "your own scorer"
    class now points at rules/12.

Family headroom after the split

rules/10 468, rules/11 415, rules/12 191 — from 12 lines of combined headroom
to 426.

The audit half of a rule we already had. An external inert-control audit spec
(seven classes beyond our five) was evaluated against the tree class by class. Two
were already covered end to end and stay rejected; the rest split into a pattern
worth naming: we had stated the build rule and never written the probe. The
library said "unit tests touch no sockets" in three places and offered no way to
find out that they do; it governed the numbers a tool prints and not the words;
it declared instruments to be controls without ever turning that recursion on
guards. Full reasoning, including the five candidates rejected as already ours, is
in docs/ADOPTION-LOG.md (entry 2026-08-04).

Nothing here is measured; no lift is claimed for any of it.

Added

  • sota-code-security rules/10 §2.10 — unearned claims are words, not just
    numbers.
    The section governed literals in reporting output. It now also
    governs the verbs: verified, confirmed, reachable from, tainted,
    sanitized, and any severity or confidence set from a constant. The test is
    "which line would have to succeed for this word to be true, and can I make that
    line fail?" — with both traps stated, because keyword-hedging every message
    saying "tainted" leaves the identical claim phrased "reachable from input", and
    "TLS certificate not verified" is correct English describing the analysed code.
  • sota-code-security rules/10 §2.14 — a control parked in observe-only mode.
    Kyverno Audit, PSA warn, WAF detection-only, SCMP_ACT_LOG, CSP
    report-only, DMARC p=none, --soft-fail. Each is correct as a rollout stage
    and inert as a destination, and renders identically to an enforcing control on
    every dashboard. Ships with an owner and an expiry, enforced somewhere that
    fails — the discipline sota-testing rules/07 §7.1 puts on a quarantined test.
    The staged ladders already existed in sota-devsecops rules/07 and
    sota-network-security rules/06; the inert-control framing did not.
  • sota-code-security rules/11 §3.4 — contract drift by interaction. Neither
    component is wrong: a producer change silently alters a layout its consumer
    depends on, and both sides' isolation tests pass while the seam is broken. The
    distinguishing feature is that no schema declares this seam, so nothing
    exists for a registry or compat check to compare — every contract rule we own
    presumes a declared contract. The high-yield trigger is a config-level backend
    or frontend swap that changes the output layout as a side effect. Rule: run the
    consumer on the producer's real output before merging.
  • sota-code-security rules/11 §7.1 — the guard that is an instance of what it
    guards.
    §7 already said an instrument is a control; it never turned the
    recursion on guards. Three forms: a coverage test whose scope is narrower than
    the population and whose predicate the defect satisfies ("auth=" in line
    passes on auth=None); a tripwire nested inside another gate's success branch;
    a denominator counting only the items that survived earlier filtering. §2.2
    catches an empty scope — this catches one merely wrong, and a predicate merely
    weak.
  • sota-code-security rules/11 §7.2 — three rules for the auditor's own
    instrument.
    Sample and read before you count (a regex over prose reported 50
    unearned claims where reading found 8); a control validated on inputs that
    cannot produce the failure proves nothing, which is the negative control's
    twin; when a wrapper reports an empty reason, go one layer down.
  • sota-code-security rules/11 §6 — run every script before reading any of
    them.
    A measurement tool nobody has executed this quarter is presumed dead
    until it prints something; the ones needing credentials, a daemon, a rules
    directory or a network fail in precisely the way a clean result looks, and one
    environment change kills them all at once.
  • sota-testing rules/02 §2.6 — prove hermeticity, don't assert it. Block
    egress and run the suite; anything that fails was not the unit test it claimed
    to be. What this catches is not a slow test but one passing for the wrong
    reason
    , its usual mechanism being a config object the SUT never reads (it
    resolves from the environment instead — rules/11 §6.7). Plus: check each
    surviving assertion against the test's own name.
  • sota-testing rules/02 §2.7 — resource optimism added as its own smell, and
    mystery guest restored to its standard external-resource sense. Ours had
    narrowed a standard catalog term to a readability defect — record rot in our own
    file, found while auditing for it.

Changed

  • rules/10 §2.13 now cites the platform mechanic. Per
    GitHub Docs,
    a skipped job reports its status as Success and "will not prevent a pull
    request from merging, even if it is a required check" — strictly worse than
    §2.13's existing "all-skipped is not all-green". A required gate whose if:
    condition stops matching turns green, not pending.
  • rules/11 §2.2 gained the instance everyone meets. go test ./... over a
    package with no test files prints ? x [no test files] and exits 0
    (verified this session). Gate on a floor for tests actually executed, never on
    the runner's exit code — and check your runner rather than assuming, since
    they differ.
  • rules/11 §3 is now "Four classes rules/10 does not cover", with the
    matching count in rules/10's header pointer; both SKILL.md routing rows and
    the README's ~62k lines figure updated (invariant 6 caught that one).

Note for the next contributor

rules/10 and rules/11 now sit at 493 and 495 of the 500-line cap. That
pair is effectively full: the next addition to this family needs a rules/12
split, not another round of trimming.