Repository navigation
ci-pipeline detect is blind to 56 code-bearing repos (and probes Deno, not Bun) #967
Description
Census finding — ci-pipeline.yml's detect is blind to 56 code-bearing repos
Measured 2026-09-22. Supersedes the earlier 47/59 extension-keyed split in this file.
Headline
Of the 106 repositories where ci-pipeline.yml's detect job finds a zero
denominator, only 43 are genuinely docs/config only. 56 non-fork repos carry a
build manifest for a language detect cannot see. Seeding the adoption ratchet at 106
would record those 56 as owing pipeline work they do not owe — 53% of the seeded
"debt" would be detector coverage, not repo debt.
What detect knows
From .github/workflows/ci-pipeline.yml:124-132 at 9c256b67, six probes:
| ecosystem | probe |
|---|---|
| Rust | Cargo.toml |
| Nickel | *.ncl |
| ReScript | rescript.json, bsconfig.json, *.res |
| V | v.mod alone (the .v extension collides with Coq and Verilog) |
| Haskell | *.cabal, stack.yaml, *.hs |
| Deno | deno.json, deno.jsonc |
There is no Bun probe. detect knows the banned JS runtime and not the
sanctioned one, against the owner's standing ruling that the estate is "all bun and
bunx now". Twelve of the undetected repos carry package.json.
Method, and why two measures
Keyed on a manifest, not an extension — the same discipline detect already applies
to V (v.mod alone, never *.v). One git/trees?recursive=1 call per repo; every path
list is saved under tmp/trees/ so later questions cost no API calls.
Run as a cross-check against the earlier extension-keyed pass. Neither is trusted
alone, because each fails in a different direction: an extension pass counts a curated
list's one stray .rb; a manifest pass counts a scaffolded repo with no source yet.
n=106 rows=107 ERR=0 truncated trees=0
manifest-keyed code-bearing: 59
extension-keyed code-bearing: 62
both agree: 58 <- the defensible core
manifest only: 1 hyperpolymath/git-seo (Project.toml, no .jl)
extension only: 4 ddraig-ssg idr:4 (no .ipkg); homebrew-tap rb:1
(a Homebrew formula); large-language-michelangelo
py:1; reflexive-ai-studio ts:1
forks inside the 58: 2 awesome-gleam (a fork; its gleam.toml is the
upstream list's own tooling), explore (a fork;
Gemfile for Jekyll, 2133 blobs)
FINAL non-fork core: 56
Forks and archived repos were checked because neither should receive a caller workflow:
6 forks estate-wide (3 in the zero set: awesome-zig, awesome-gleam, explore) and
3 archived estate-wide, 0 in the zero set. All 456 census repos appear in the org
listings, so the archived probe has full coverage. The correction is small — it does not
rescue the headline, and saying so is the point.
Per-language mix of the core 58
| manifest | repos |
|---|---|
Julia (Project.toml) |
29 |
Bun/JS (package.json, bunfig.toml, bun.lockb) |
12 |
Zig (build.zig) |
10 |
Idris2 (*.ipkg) |
10 |
Lean (lakefile.*, lean-toolchain) |
4 |
Elixir (mix.exs) |
4 |
Agda (*.agda-lib) |
2 |
Ada (alire.toml, *.gpr) |
2 |
| Ruby / PHP / Isabelle / Gleam | 1 each |
The finding rests on a contradiction inside standards, not on my judgement
.githooks/validate-spdx.sh:37-38 — the estate's own authority on "is this a source
file" — already classes these as source:
*.rs|*.res|*.js|*.ts|*.sh|*.bash|*.zig|*.ex|*.exs|*.gleam|
*.ml|*.mli|*.adb|*.ads|*.ncl|*.toml|*.yaml|*.yml|*.scm
zig, ex, exs, gleam, ml, mli, adb, ads are every one of them invisible
to detect. So standards simultaneously demands an SPDX header from these files as
source code, and declares the repos containing them to have no ecosystem.
Two arms
Arm A — extend detect first, then seed at ~43. Add manifest probes for Julia, Zig,
Idris2, Elixir, Lean, Agda, Ada, Gleam and Bun. The ledger then records real debt
only. This is the elegant long-term arm: it makes the ratchet's denominator mean what it
says, and it closes the Bun/Deno inversion at the same time.
Arm B — seed at 106 today, pay down by extending detect later. Faster to land, but
the ledger's first entry is 53% wrong, and a shrink-only ledger makes that number the
baseline everything is measured against.
Caveats, stated because they bound the claim
- Default branches only. A repo whose work lives on a feature branch reads as
docs-only here. - A manifest is evidence of intent, not of a working build.
git-seohas a
Project.tomland no.jlat all. - The 43 "docs-only" is a floor, not a proof of emptiness — it means no manifest and
no source extension in the list above, on the default branch, today.
Acceptance criteria
-
detectgains manifest probes for Julia, Zig, Idris2, Elixir, Lean, Agda, Ada and
Gleam, each keyed on a manifest (never a bare extension —.valready
demonstrates why). -
detectgains a Bun probe (package.json+bunfig.toml/bun.lockb). Today
it probes Deno, which is banned, and not Bun, which is mandated. - Re-running the census reduces zero-denominator repos from 106 to ≤ 50, and
the per-ecosystem denominator is printed asdetectalready does for the six it
knows. - The adoption ledger is seeded from the post-extension census, so no repo appears
in it purely becausedetectcould not see its language. - A control repo per newly-probed ecosystem is asserted green (positive control) and
a docs-only repo still failsdetect(negative control) — a probe that matches
everything is not a probe.
Reproduce
# per-repo path lists (1 API call per repo, cached under tmp/trees/)
gh api "repos/$REPO/git/trees/$BRANCH?recursive=1" \
| jq -r '(if .truncated then "#TRUNCATED" else "#COMPLETE" end), (.tree[]|select(.type=="blob")|.path)'
# what detect knows, at the base commit
git show 9c256b67:.github/workflows/ci-pipeline.yml | sed -n '124,132p'
# the contradiction
sed -n '35,41p' .githooks/validate-spdx.sh
# forks / archived
awk -F'\t' 'NR>1 && $3=="true"' census.tsv | wc -l # 6 estate-wide
gh repo list hyperpolymath --limit 600 --json nameWithOwner,isArchivedCross-refs: #787 (decision surface), #948 (the adoption ratchet this seeds), #956
(blocking requires a required_status_checks ruleset rule, not merely a caller).
🤖 Generated with Claude Code
Activity
hyperpolymath commentedon Sep 22, 2026
Correction to this issue's framing, before #970 lands.
This issue says the pipeline "probes Deno, not Bun", which implies the cure is
to swap one for the other. That is wrong, and #970 deliberately does not do it.
The Deno probe is not a lint gate. It is the trigger for the
denorefusal job — the enforcement teeth of the standing no-Deno ruling, with
slugs still live in
.machine_readable/deno-allow.txt. Removing it would not"drop support for a banned runtime"; it would silently un-enforce the ban,
because the refusal simply stops firing and the repository goes green. The
probe is kept, and a comment now says so at the site.
The real gap was only ever the absence of a Bun probe.
Second correction — scope. This issue implies extending
detectshrinksthe red set. It does not; it classifies it. No lint job is added for any of
the twelve new ecosystems, so after #970 the adoption ledger holds two classes:
no ecosystem at all, and detected but unsupported. Two cures, both now
countable. The acceptance criteria below should be read as "the debt is
separable", not "the debt is smaller".
Addressed by #970.
feat(ci): detect unsupported ecosystems without silencing the zero-de…
fix(ci-pipeline): make report the single judge, and read the ledger a…
hyperpolymath commentedon Sep 22, 2026
Dry-run wave complete — 5/5 callers landed, and the result corroborates this issue
All five pilot callers are now merged and pinned to
ci-pipeline.yml@ed5e3f65at theirdefault branches (verified by reading default-branch contents, not PR state).
Verdicts
cicd-squabbler35774765633tropical-types35772373157modshells35772292238ddraig-ssg35772160266AcceleratorGate.jl35772954859⚠ The green is not what it looks like — this is the issue, measured
detectsucceeded in every repo, and in four of five it selected zeroecosystems. Those repos' only substantive jobs were the language-agnostic arms
(
SAST (semgrep),Secret scanning/*,Detect ecosystems). Every per-language armskipped.
cicd-squabblertropical-typesmodshellsddraig-ssgAcceleratorGate.jlThe pipeline ships arms for Rust, Haskell, ReScript, V, Nickel, Deno. The pilot's
actual languages are Idris, Lean, Isabelle, Julia, Ada, Nushell, Scheme, Zig, TeX —
the intersection is empty for four of the five.
cicd-squabbleris the only repo thatexercised a language arm, and only because it happens to carry Rust and Nickel.
So the wave's
successis honest at the security layer and vacuous at thequality layer for 4/5 repos. A green here must not be read as "this repo is clean";
it means "nothing in this repo was checked".
Consequence for
.machine_readable/pipeline-allow.txtDoctrine is that an entry is earned by observation — the pipeline ran there and went
red. Nothing went red, so zero entries are earned and the ledger stays empty. That
is the correct outcome, but for 4/5 repos it is earned by non-coverage rather than by
cleanliness, and the ledger cannot distinguish those two states.
Suggested follow-ups (not actioned here): have
detectreport the ecosystems itconsidered and rejected, and treat "zero ecosystems selected in a code-bearing repo"
as a distinct, visible outcome rather than a silent green.
Also filed while landing this wave: #992 (the
check-lock-sync.sh/ GitHub startupdeadlock that blocked
cicd-squabbler's caller).🤖 Generated with Claude Code
https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR
test(k9): prove K9-N002, K9-E000 and the K9-C001 consequence can fire (…