Skip to content

Apache-2.0, the Agent Governance Leaderboard, and an honest competitor score - #35

Merged
bkd-dotcom merged 4 commits into
mainfrom
open-core-relicense
Sep 1, 2026
Merged

Apache-2.0, the Agent Governance Leaderboard, and an honest competitor score#35
bkd-dotcom merged 4 commits into
mainfrom
open-core-relicense

Conversation

@bkd-dotcom

@bkd-dotcom bkd-dotcom commented Sep 1, 2026

Copy link
Copy Markdown
Member

Three changes, in increasing order of how much they matter.

1. Apache-2.0 — this repo has no strings, deliberately

Signetry goes open core:
the engine (core) is source-available under
BUSL-1.1 and converts to Apache-2.0 on 2030-08-31; everything you integrate with,
including this repo, is Apache-2.0.

A benchmark nobody can freely run, audit, and reproduce is worthless as evidence, so
the eval suite carries the most permissive licence of anything in the platform. Fork
it, re-run it, publish results that disagree with ours.

The CLA is kept — open core means code sometimes has to move across the line — but its
fallback grant is now non-exclusive, so a contributor never loses the right to use
their own contribution.

2. A competitor was scored on cases it was never run on

This one is a correction against a named third party, so it is called out on its own.

captured_corpus_adapter returned an empty finding set for any case absent from a
scanner's capture, and the scorer counted that as a miss. Captures are point-in-time;
this corpus grows. The 8 cases added in the 52 → 60 expansion were charged to
claude-code-security-review as failures — 5 of them vulnerable — so its published
recall read 81% (38/47) when the honest figure over the cases it was actually given
is 90% (38/42).

That is exactly the defect this suite calls out everywhere else — an unearned number —
except pointed outward at someone else's tool, and in our favour by about nine points.

An absent case is now excluded from that scanner's denominator. A case that is present
but lists no findings is still a genuine miss; only "never run" is excused, and a test
pins that distinction. Every table prints Cases scored per scanner and lists the
excluded ids, because silently excluding them would be its own dishonesty once the two
scanners are no longer scored over the same set.

3. The Agent Governance Leaderboard

Detection is table stakes — every scanner publishes a recall number. The axis nobody
publishes is governance: when the repository itself is hostile, does the agent's
change still get admitted, and what does the defense cost in benign work?

docs/LEADERBOARD.md publishes both axes on one page and takes
third-party submissions
, so the governance axis can become a real comparison instead
of a self-report. docs/SUBMITTING.md covers submitting an attack
that beats the governed pipeline, and submitting a system — including one that beats
Signetry
.

A leaderboard whose author is also its referee is only worth something if the rules are
mechanical, so the renderer enforces three in code:

  • an unmeasured number renders as , never 0%; a rate submitted without its
    denominator is refused and the reason is printed on the page
  • reproduced and self-reported rows never share a table
  • every rate is printed beside the sample size it was computed over

Also fixed here: Report.by_category() reported 0 for metrics with nothing behind
them, so an attack-only category showed Utility 0% — reading as "Signetry destroyed
utility here". A measured zero is still 0%; the rule is no unearned numbers, not no
zeros.

Verification

  • 95 tests pass (58 of them at commit 2 in isolation, so each commit stands alone);
    ruff check clean
  • 32 new tests on the leaderboard renderer, one per rule — including that bool is a
    subclass of int (True must not be accepted as an ASR of 100%) and that NaN
    passes isinstance and fails every comparison, so a naive 0 <= x <= 1 guard would
    render it as nan%
  • 8 new tests on capture coverage, 5 on unmeasured category metrics
  • all four generated pages regenerated and inspected

CI

leaderboard.yml regenerates the page weekly, on release, and when an entry or scenario
changes; benchmark.yml moves onto the same schedule. Both pages were previously
release-only, so published numbers could be months stale while reading as current.

benchmark.yml's release-time push could never have worked: on a release event the
checkout is detached at the tag, so git push HEAD:$GITHUB_REF_NAME targeted
refs/tags/vX.Y.Z and the failure was swallowed by || echo "push skipped". Both
workflows now push the default branch explicitly, share a concurrency group, and run
their regression gate last — so a broken defense is published and then fails the
run, rather than failing first and leaving a stale page up.

Signetry moves to an open-core model. This repository is part of the
integration surface, so it is now Apache-2.0: use it, fork it, ship it
commercially, no permission needed. The engine (Signetry/core) is
source-available under BUSL-1.1 and converts to Apache-2.0 on 2030-08-31.

Until now every repo in the org was "All Rights Reserved" with no LICENSE
file, which GitHub reports as no license at all. That is the strictest
possible default: it blocks corporate evaluation outright and makes outside
contribution legally murky. This fixes that.

What changed:

- LICENSE (Apache-2.0) added, so GitHub detects and displays it.
- README, CONTRIBUTING, CLA, CONTRIBUTORS and the CLA bot comment no longer
  claim the project is "not open source" or withhold usage rights.
- The CLA is kept, because open core means code moves across the licence
  line and relicensing needs those rights. Its fallback licence grant is now
  non-exclusive: the previous *exclusive* grant would have stripped
  contributors of the right to use their own contribution, contradicting the
  rights the LICENSE grants everyone. The CLA text is now identical across
  all Signetry repos so the legal terms cannot drift per-repo again.
- Community health files (SECURITY.md, CODE_OF_CONDUCT.md, issue templates)
  where they were missing.

No functional or API change.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Signetry Reviewer — 🟣 Escalate to a designated reviewer

Escalate to a designated reviewer — this PR touches security-sensitive surface (.github/workflows/benchmark.yml, .github/workflows/cla.yml, .github/workflows/eval.yml, .github/workflows/leaderboard.yml). No blocking issue was found automatically, but a human owner should sign off.

Deterministic gates (the authority)

Gate Status
Required status check — unknown
Secret scan ✅ clean
CI permission / OIDC ✅ no forbidden change
Dependency skew ✅ ok
All green

Findings (6, 0 blocking)

  • 🟡 Change touches a protected path: .github/workflows/benchmark.yml .github/workflows/benchmark.yml (via cross-check)
    • .github/workflows/benchmark.yml matches a protected pattern (.github/workflows/*). Changes here alter shared/foundational surface and warrant a designated reviewer.
    • Fix: Route to a code owner / architecture reviewer.
  • 🟡 Change touches a protected path: .github/workflows/cla.yml .github/workflows/cla.yml (via cross-check)
    • .github/workflows/cla.yml matches a protected pattern (.github/workflows/*). Changes here alter shared/foundational surface and warrant a designated reviewer.
    • Fix: Route to a code owner / architecture reviewer.
  • 🟡 Change touches a protected path: .github/workflows/eval.yml .github/workflows/eval.yml (via cross-check)
    • .github/workflows/eval.yml matches a protected pattern (.github/workflows/*). Changes here alter shared/foundational surface and warrant a designated reviewer.
    • Fix: Route to a code owner / architecture reviewer.
  • 🟡 Change touches a protected path: .github/workflows/leaderboard.yml .github/workflows/leaderboard.yml (via cross-check)
    • .github/workflows/leaderboard.yml matches a protected pattern (.github/workflows/*). Changes here alter shared/foundational surface and warrant a designated reviewer.
    • Fix: Route to a code owner / architecture reviewer.
  • 🟡 Change touches a protected path: .github/workflows/reviewer.yml .github/workflows/reviewer.yml (via cross-check)
    • .github/workflows/reviewer.yml matches a protected pattern (.github/workflows/*). Changes here alter shared/foundational surface and warrant a designated reviewer.
    • Fix: Route to a code owner / architecture reviewer.
  • 🟡 Change touches a protected path: pyproject.toml pyproject.toml (via cross-check)
    • pyproject.toml matches a protected pattern (pyproject.toml). Changes here alter shared/foundational surface and warrant a designated reviewer.
    • Fix: Route to a code owner / architecture reviewer.

Sensitive surface

This PR changes security-sensitive paths that warrant a designated reviewer:

  • .github/workflows/benchmark.yml
  • .github/workflows/cla.yml
  • .github/workflows/eval.yml
  • .github/workflows/leaderboard.yml
  • .github/workflows/reviewer.yml

Merge

A designated reviewer / code owner should sign off before merge (sensitive surface).

This review is advisory. It never merges on its own judgement — the deterministic gates + a human are the authority. Findings can have false negatives; a green bot verdict is not a guarantee.

The published head-to-head understated a named competitor by ~9 recall
points, and it did so on evidence that does not exist.

`captured_corpus_adapter` returned an empty finding set for any case
absent from a scanner's capture, and the scorer counted that as a miss.
Captures are point-in-time; this corpus grows. The 8 cases added in the
52 -> 60 expansion were therefore charged to
claude-code-security-review as failures — 5 of them vulnerable — so its
published recall read 81% (38/47) when the honest figure over the cases
it was actually given is 90% (38/42). The old module docstring stated
the bug outright: "recorded as a miss, never fabricated".

That is precisely the defect this suite calls out everywhere else, an
unearned number, except pointed outward at someone else's tool and in
our favour.

- An absent case now returns `ran=False`, scores as `covered=False`, and
  is excluded from that scanner's recall, false positives, and the
  per-family / per-language breakdowns. A case that IS present but
  lists no findings is still a genuine miss; only "never run" is
  excused, and a test pins that distinction.
- Every table prints "Cases scored" per scanner and lists the excluded
  ids under Notes. Silently excluding them would be its own dishonesty
  now that the two scanners are no longer scored over the same set.
- `CorpusScore.recall` is `float | None` and returns None rather than
  0.0 when nothing was measured. The `--min-recall` gate now fails on an
  unmeasured recall instead of passing it by default.

The fix also corrects the by-language rows: go and java each read 4/6
because two of the missing cases were the new ones.

8 tests added.
Detection is table stakes — every scanner publishes a recall number. The
axis nobody publishes is governance: when the repository itself is
hostile, does the agent's change still get admitted, and what does the
defense cost in benign work? docs/LEADERBOARD.md publishes both axes on
one page and takes third-party submissions, so the governance axis can
become a real comparison instead of a self-report.

A leaderboard whose author is also its referee is only worth something
if the rules are mechanical, so the renderer enforces three in code
rather than by good intentions:

- an unmeasured number renders as an em dash and never as 0%. A rate
  submitted without its denominator is refused and shown as unmeasured,
  with the reason printed on the page.
- reproduced and self-reported rows never share a table. We ran it here,
  or we did not, and the page says which.
- every rate is printed beside the sample size it was computed over. The
  corpus is 5 scenarios and the page says so in prose.

Validation is where the honesty actually lives, so it is pinned by 32
tests, one per rule — including that bool is a subclass of int (True
must not be accepted as an ASR of 100%) and that NaN passes isinstance
and fails every comparison, so a naive 0 <= x <= 1 guard lets it render
as "nan%".

- `signetry-eval leaderboard` (--with-detection, --json, --entries).
  Exits non-zero if any governed defense failed. If the detection axis
  cannot run, it is omitted with a warning rather than published empty.
- leaderboard/entries/ takes one JSON file per system. The ungoverned
  baseline ships as its own visible row rather than being implied by a
  column heading — a control condition you cannot see is not a control.
- docs/SUBMITTING.md covers both contribution paths, including
  submitting a system that beats Signetry. A leaderboard only its author
  can win is marketing, and everyone can tell.

Also fixed: Report.by_category() reported 0 for metrics with nothing
behind them, so an attack-only category showed "Utility 0%" — reading as
"Signetry destroyed utility here" — and the utility category showed an
ASR of 0% over zero attacks. Both are None now, render as an em dash,
and serialize as null. A measured zero is still 0%: the rule is no
unearned numbers, not no zeros, and a test pins that too.

CI: leaderboard.yml regenerates the page weekly, on release, and when an
entry or scenario changes. benchmark.yml moves onto the same schedule —
both pages were release-only, so published numbers could be months stale
while reading as current. Each workflow owns exactly one page so every
"Generated by ..." header names the workflow that actually wrote it, and
both share a concurrency group because they both push to main.

benchmark.yml's release-time push could never have worked: on a release
event the checkout is detached at the tag, so `git push HEAD:$REF_NAME`
targeted refs/tags/vX.Y.Z and the failure was swallowed by `|| echo
"push skipped"`. Both workflows now check out and push the default
branch explicitly. The regression gate runs last in both, so a broken
defense is published and then fails the run, rather than failing first
and leaving the stale page up.
@bkd-dotcom bkd-dotcom changed the title chore: relicense to Apache-2.0 (Signetry goes open core) Apache-2.0, the Agent Governance Leaderboard, and an honest competitor score Sep 1, 2026
The page says the signetry-core row is "measured live by `signetry-eval run` in this
repository's CI", but recorded `version: null` for it. A reader cannot reproduce a
result without knowing which build produced it, and reproducibility is the one thing
this leaderboard exists to support — it is what separates "reproduced here" from
"self-reported" in the Evidence column.

The `--version` flag already existed and was plumbed through to both renderers. CI
simply never passed it, so the field was silently unmeasured on every run.

CI now reads the label from the installed package rather than hardcoding it, because
a stale label is worse than the null it replaces: it would assert a specific build
was tested when it wasn't. If the version cannot be read, the argument is omitted and
the row degrades to "not measured" — the same rule this leaderboard applies to every
rate it prints.

Two tests: the label reaches both the markdown row and the JSON envelope, and an
unknown version stays null rather than rendering an empty backtick label.

Regenerated the committed page and envelope. Verified first that a no-version regen
reproduces the committed files byte-for-byte, so the diff here is exactly the one
line each that the version adds and nothing else drifted.
@bkd-dotcom

Copy link
Copy Markdown
Member Author

Pushed fix(leaderboard): name the version that produced the live row (cdc1357).

The page says the signetry-core row is "measured live by signetry-eval run in this repository's CI" — but recorded version: null for it. A reader cannot reproduce a result without knowing which build produced it, and reproducibility is what separates reproduced here from self-reported in the Evidence column.

The --version flag already existed and was plumbed to both renderers; CI simply never passed it, so the field was silently unmeasured on every run.

CI now reads the label from the installed package rather than hardcoding it — a stale label would be worse than the null it replaces, since it asserts a specific build was tested when it was not. If the version cannot be read the argument is omitted and the row degrades to "not measured", the same rule the leaderboard applies to every rate it prints.

Before regenerating the committed page I confirmed a no-version regen reproduces both committed files byte-for-byte, so the diff is exactly the one line each the version adds and nothing else drifted.

@bkd-dotcom
bkd-dotcom merged commit 0da974d into main Sep 1, 2026
6 checks passed
@bkd-dotcom
bkd-dotcom deleted the open-core-relicense branch September 1, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant