The finding
The detect job's Nickel probe matches RSR template boilerplate, so the
zero-denominator refusal — the pipeline's central anti-vacuity guard — does not
fire on 9 of 24 sampled repositories it was built for.
ci-pipeline.yml's detect step counts *.ncl into TOTAL. Every repo seeded
from the RSR template carries the same six-file k9 Nickel set, so TOTAL
is non-zero for a reason that has nothing to do with the repository's ecosystem.
The nickel job then runs, lints six template config files, and the pipeline
reports success while the repo's actual language is never examined.
The workflow's own comment states the standard this violates:
Folding them into TOTAL would lift it above zero and SILENCE the
zero-denominator refusal below while nothing whatsoever had been checked.
That is a vacuous gate, and a vacuous gate is worse than an honest blind
spot: the blind spot reports nothing, the vacuous gate reports success.
That reasoning is correct and is exactly what happens here — the boilerplate
does the lifting instead of the unsupported probes.
Evidence
Deterministic sample: every 5th live repo whose primary language is one the
pipeline detects but cannot check (Julia, Elixir, Idris, Zig, Ada, Agda,
Gleam, Lean, Isabelle, Ruby, PHP) — 24 repos of 134 such. Probes replicated
exactly from detect, run against real default-branch trees via
gh api repos/<r>/git/trees/<default>?recursive=1, not a local grep.
| verdict |
repos |
RUNS-ncl-only (denominator is Nickel alone) |
14 |
RUNS-real |
6 |
REFUSE-unsupported |
3 |
REFUSE-none |
1 |
Of the 14, 9 have zero repo-owned Nickel — every matched .ncl is template:
hyperpolymath/candy-crash template_ncl=6 own_ncl=0 unsupported_markers=2
hyperpolymath/Cliometrics.jl template_ncl=12 own_ncl=0 unsupported_markers=3
hyperpolymath/fireflag template_ncl=6 own_ncl=0 unsupported_markers=1
hyperpolymath/julia-ecosystem template_ncl=6 own_ncl=0 unsupported_markers=62
hyperpolymath/ochrance template_ncl=6 own_ncl=0 unsupported_markers=7
hyperpolymath/polyglot-formalisms-gleam template_ncl=6 own_ncl=0 unsupported_markers=1
hyperpolymath/resource-record-fluctuator template_ncl=7 own_ncl=0 unsupported_markers=1
hyperpolymath/SMTLib.jl template_ncl=6 own_ncl=0 unsupported_markers=2
hyperpolymath/volumod template_ncl=6 own_ncl=0 unsupported_markers=2
The worst case is hyperpolymath/julia-ecosystem: it would lint 6
template files, report success, and leave 62 Julia markers unexamined. The
refusal added in #970 never fires.
The boilerplate is identical across unrelated repos — same six basenames, only
the directory prefix differs:
julia-ecosystem .machine_readable/self-validating/{template-hunt,template-kennel,template-yard}.k9.ncl + examples/{ci-config,project-metadata,setup-repo}.k9.ncl
SMTLib.jl .machine_readable/self-validating/ ... identical six
polyglot-formalisms-gleam contractiles/k9/ ... identical six
Across the sample: 134 .ncl files in 19 repos, of which only 13 (9.7%) are
repo-owned; in 12 of the 19, every single .ncl is template.
Why this is not cosmetic
This is the failure mode #970 was written to prevent, reintroduced through a
different door. #970 correctly refused to fold unsupported markers into
TOTAL; but TOTAL was already being lifted by files that are equally not a
check of the repository. The guard is intact and simply never reached.
It also silently mis-scopes the planned rollout: these 9 repos would receive a
blocking caller and go green, when the honest outcome is a
detected-unsupported refusal and a ledger entry.
Cure options (owner's call — not implemented here)
- Exclude the template set from the probe (
.machine_readable/,
contractiles/, *.k9.ncl) — narrow, matches the existing
manifests-not-extensions doctrine.
- Count only
.ncl outside those paths toward TOTAL, still linting all of
them if the job runs.
- Treat "denominator is template-only" as equivalent to zero.
Acceptance criteria
Scope note
Found while censusing candidates for the #967 adoption rollout. This blocks
seeding the adoption ledger by detection, because the detector currently
reports "checkable" for repos where nothing meaningful is checked — the ledger
would record the wrong debt.
Refs #967, #970, #956.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR
The finding
The
detectjob's Nickel probe matches RSR template boilerplate, so thezero-denominator refusal — the pipeline's central anti-vacuity guard — does not
fire on 9 of 24 sampled repositories it was built for.
ci-pipeline.yml'sdetectstep counts*.nclintoTOTAL. Every repo seededfrom the RSR template carries the same six-file k9 Nickel set, so
TOTALis non-zero for a reason that has nothing to do with the repository's ecosystem.
The
nickeljob then runs, lints six template config files, and the pipelinereports success while the repo's actual language is never examined.
The workflow's own comment states the standard this violates:
That reasoning is correct and is exactly what happens here — the boilerplate
does the lifting instead of the unsupported probes.
Evidence
Deterministic sample: every 5th live repo whose primary language is one the
pipeline detects but cannot check (Julia, Elixir, Idris, Zig, Ada, Agda,
Gleam, Lean, Isabelle, Ruby, PHP) — 24 repos of 134 such. Probes replicated
exactly from
detect, run against real default-branch trees viagh api repos/<r>/git/trees/<default>?recursive=1, not a local grep.RUNS-ncl-only(denominator is Nickel alone)RUNS-realREFUSE-unsupportedREFUSE-noneOf the 14, 9 have zero repo-owned Nickel — every matched
.nclis template:The worst case is
hyperpolymath/julia-ecosystem: it would lint 6template files, report success, and leave 62 Julia markers unexamined. The
refusal added in #970 never fires.
The boilerplate is identical across unrelated repos — same six basenames, only
the directory prefix differs:
Across the sample: 134
.nclfiles in 19 repos, of which only 13 (9.7%) arerepo-owned; in 12 of the 19, every single
.nclis template.Why this is not cosmetic
This is the failure mode #970 was written to prevent, reintroduced through a
different door. #970 correctly refused to fold unsupported markers into
TOTAL; butTOTALwas already being lifted by files that are equally not acheck of the repository. The guard is intact and simply never reached.
It also silently mis-scopes the planned rollout: these 9 repos would receive a
blocking caller and go green, when the honest outcome is a
detected-unsupported refusal and a ledger entry.
Cure options (owner's call — not implemented here)
.machine_readable/,contractiles/,*.k9.ncl) — narrow, matches the existingmanifests-not-extensions doctrine.
.ncloutside those paths towardTOTAL, still linting all ofthem if the job runs.
Acceptance criteria
hyperpolymath/julia-ecosystem, the pipeline REFUSES with thedetected-unsupported message instead of running the
nickeljob.own_ncl=0 && nonncl=0 && unsupported>0class (currently 9).hyperpolymath/laminar(3 repo-owned.nclof 4) still reportshas_nickel=trueand still runs thenickeljob. Likewisehyperpolymath/zerotier-k8s-link(4 own of 11)..ncl, so the number printed cannot be mistaken for a real one.Scope note
Found while censusing candidates for the #967 adoption rollout. This blocks
seeding the adoption ledger by detection, because the detector currently
reports "checkable" for repos where nothing meaningful is checked — the ledger
would record the wrong debt.
Refs #967, #970, #956.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR