Consumer inventory — proving who calls this hub#

Why a grep was not enough#

Three deletions in this repository have had to be restored. Every one of them was justified the same way: a search found no caller. The search was right about the machine it ran on and wrong about the world — the caller lived in a repository that was not checked out there.

That failure mode cannot be fixed by grepping harder. A hub cannot see its consumers, so the list of them has to be declared, kept under review, and walked from a clean clone every time the question is asked.

The consumer inventory#

Two files, one workflow.

Piece

Path

The declared list of consumers and of the entry-point classes

.github/consumers.json

The checker that clones them and grades the entry points

linux/scripts/verify_consumer_inventory.py

The weekly run that produces the report

.github/workflows/consumer-inventory.yml

A failed weekly run files or updates a GitHub issue titled consumer-inventory: dangling hub references, labelled documentation, with out/consumer-inventory.md as the body (the issue-filing step in that workflow, if: failure(), job-scoped issues: write). It updates the open issue instead of opening a second one, and it does not suppress the failure: the run is still red. This exists because the answer changes when a consumer changes, which this repository never sees — so the only reader of a Monday-morning red run would otherwise be whoever happened to open the Actions tab.

Run it against clones:

python3 linux/scripts/verify_consumer_inventory.py --report out/consumer-inventory.md

Run it against checkouts you already have, with no network at all:

python3 linux/scripts/verify_consumer_inventory.py --offline \
    --local-root /c/GitHub --report out/consumer-inventory.md

--local-root DIR picks up DIR/<name> for every consumer whose name matches a directory there; --local NAME=PATH names one explicitly. Under --offline a consumer with neither is a failure, not a skip.

What counts as reachable#

The report grades every entry point into exactly one of four states, and the distinction that matters is between the second and the third:

Verdict

Meaning

What it licenses

reached by a consumer

Some repository other than this one executes it

Changing it is a breaking change for the named repos

hub-internal only

This repository executes it; nobody else names it

Refactor freely; it is not published surface

mentioned, never reached

Named in prose or in a comment, run by nothing

Deleting it breaks a document, not a build

named by nobody

No repository in the inventory names it at all

The candidate list for deletion

A hit is reached when it sits somewhere that runs: a uses: value, a run: line, a source/Import-Module/include() call. It is mentioned when it sits in a Markdown or reStructuredText page, in a .allow row, or on a line that opens a comment. A hub script named in a consumer’s changelog keeps nothing alive, and a report that counted it would hand back the same false confidence the grep did.

Entry points are declared, not guessed#

entry_point_classes in the inventory expands globs into the concrete entry points to grade. A class that expands to zero files fails the run — a silently empty class would quietly shrink the surface being examined.

Two classes carry an alias, because their entry points are not reached by path at all:

  • windows-module — a consumer writes Resolve-BuildModulePath -Name 'WindowsBuild.Common'. Grading on the path alone reported all twenty-two modules dead.

  • cmake-module — a consumer puts the hub’s cmake/ on CMAKE_MODULE_PATH and writes include(Sanitizers).

An alias hit is discarded when the consumer owns a file of that basename: its own module shadows the hub’s, so the reference is not evidence about the hub.

What makes the run fail#

Only integrity failures — never the verdicts themselves. The counts are a report for a human; these are defects:

  1. A consumer cannot be obtained. The clone failed, the checkout is missing, or --offline was given without a local path for it. A skip here would turn “no caller found” into a lie shaped exactly like the truth.

  2. A consumer is not a git checkout, or has no tracked files. A partial tree would under-report callers.

  3. A dangling reference. An executable line in any scanned repository names a hub path that does not exist. Comments are exempt: a comment recording that a path was removed is prose about the hub, not a broken call.

  4. The inventory does not parse, or declares no self consumer. Without a row marked self, nothing can separate hub-internal use from a real caller.

Keeping the list true#

The inventory is only as good as its consumers array, so the file carries a second array, unconfirmed, for repositories that an audit has named but nobody has verified. The checker prints how many are still open on every successful run. Each row must end one of two ways: promoted into consumers with confirmed: true, or deleted because the repository does not exist.

Treat a named by nobody verdict as a lead, not a licence, while that array is non-empty. It is a statement about the repositories in the list — nothing more.