Repository navigation
Description
Finding
static/ is 23M. 16.2M of it (70%) is unreachable — published to every
visitor on every deploy as bytes nothing on the site can request. Nothing in the
test suite enumerates static/, so no gate has ever noticed.
Verified at 00b44df, node v26.8.2, 2026-09-19.
| directory | files | size | referenced |
|---|---|---|---|
static/images/ |
16 | 16M | 0 |
static/social/ |
35 | 148K | 0 |
static/favicons/ |
5 | — | 4 of 5 (favicon.ico orphaned) |
These are contribute.cncf.io template leftovers, not end-user site assets.
The tell is in the filenames: tag-contributor-strategy-icon-color.png,
TAG-CS_roadmap_visual.png, and a video-thumbnails/ directory holding 14M
alone across 7 PNGs for TAG Contributor Strategy talks that this site never
links to. The single largest file, from-maori-to-deaf-engineers.png, is 3.3M.
Three specific pieces of drift:
static/social/is wholly superseded.src/theme/Footer/index.js:10-58
inlines its social icons as literal JSX<svg>elements. All 35 standalone
SVGs — the fullboxed-*set plus singles — are dead.- A byte-identical duplicate.
static/images/CNCF_logo_white.svgand
static/img/cncf_logo_white.svgshare md56465b4f06d8df6eeb378b8d7b148e7c3.
Only theimg/copy is live (referenced fromsrc/theme/Footer/index.js). - A split favicon story.
docusaurus.config.js:18sets
favicon: 'img/favicon.ico', sostatic/favicons/favicon.icois orphaned
while its four siblings are live viastatic/manifest.jsonand
docusaurus.config.js:50. The two.icofiles are a trap for the next
person who edits one and expects the site to change.
Why existing gates miss it
- Docusaurus copies
static/intobuild/verbatim. There is no reachability
analysis, andonBrokenLinks: 'throw'governs page routes, not assets. - This is the reverse direction of the static-asset contract. Open PR test: verify local /img static asset references resolve under static/ (tests/static-assets.test.mjs) #289
(tests/static-assets.test.mjs) checks the forward direction — that
/imgreferences written indocs/,blog/,src/anddata/*.json
resolve understatic/. A file nothing points at is invisible to it. - [quality] src/css/custom.css <-> static/fonts/ contract is untested; 27 italic faces (764K) shipped but never declared #317 claims the orphan invariant for
static/fonts/only. This issue is
disjoint from it by directory, and 21x larger by weight (16.2M vs 764K). - No other open PR reads
static/images/,static/social/orstatic/favicons/.
Recommendation
Two boxes. Both land in one PR — the test is red until the deletion lands,
so shipping it alone would knowingly redden CI.
- Delete the unreachable assets. Recommended fix is pure deletion: zero
visual delta, because nothing resolves to any of these paths today.
git rm -r static/images static/social
git rm static/favicons/favicon.ico If a maintainer wants to *keep* `static/images/video-thumbnails/` for
planned content, say so on this issue and the guard below should gain an
explicit allowlist entry rather than being dropped — the point is that the
exception becomes written down instead of silent.
- Add the guard, verbatim, as
tests/static-orphans.test.mjs:
```javascript
import assert from 'node:assert/strict';
import test from 'node:test';
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join, relative } from 'node:path';
const repoRoot = new URL('..', import.meta.url).pathname;
const staticRoot = join(repoRoot, 'static');
// Directories under static/ that are published but whose contents are only
// ever reached through a reference written somewhere in the source tree.
// Docusaurus copies all of static/ into build/, so an unreferenced file is
// shipped to every visitor as bytes nothing can request.
const GUARDED_DIRS = ['images', 'social', 'favicons'];
// Source trees that may legitimately reference a static asset.
const SOURCE_DIRS = ['docs', 'blog', 'src', 'data', 'adr'];
const SOURCE_FILES = [
'docusaurus.config.js',
'sidebars.js',
'static/manifest.json',
];
const SOURCE_EXTENSIONS = new Set([
'.js',
'.jsx',
'.ts',
'.tsx',
'.md',
'.mdx',
'.json',
'.css',
'.yml',
'.yaml',
'.html',
]);
function walk(dir) {
const out = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const full = join(dir, entry.name);
if (entry.isDirectory()) out.push(...walk(full));
else out.push(full);
}
return out;
}
function readSourceCorpus() {
const files = [];
for (const dir of SOURCE_DIRS) {
let stats;
try {
stats = statSync(join(repoRoot, dir));
} catch {
continue;
}
if (stats.isDirectory()) files.push(...walk(join(repoRoot, dir)));
}
for (const file of SOURCE_FILES) files.push(join(repoRoot, file));
return files
.filter((f) => SOURCE_EXTENSIONS.has(f.slice(f.lastIndexOf('.'))))
.map((f) => {
try {
return readFileSync(f, 'utf8');
} catch {
return '';
}
})
.join('\n');
}
const corpus = readSourceCorpus();
test('the source corpus is non-empty, so the orphan checks cannot pass vacuously', () => {
assert.ok(
corpus.length > 10_000,
`expected a substantial source corpus, read ${corpus.length} chars`,
);
});
for (const dir of GUARDED_DIRS) {
test(`every file in static/${dir}/ is referenced from the source tree`, () => {
const base = join(staticRoot, dir);
let entries;
try {
entries = walk(base);
} catch {
return; // directory removed entirely: nothing to orphan
}
const orphans = entries.filter((file) => {
const webPath = `/${relative(staticRoot, file).split('\\').join('/')}`;
// Match the absolute web path, or the same path without its leading
// slash (Docusaurus accepts `img/favicon.ico` in config fields).
return !corpus.includes(webPath) && !corpus.includes(webPath.slice(1));
});
assert.deepEqual(
orphans.map((f) => relative(repoRoot, f)).sort(),
[],
`unreferenced files under static/${dir}/ are published but unreachable`,
);
});
}
```
### Verification already done
- Against `main` at `00b44df` the guard fails with **exactly** the 16 + 35 + 1
files tabled above — no more, no fewer. The failure output is the cleanup
checklist.
- After `rm -rf static/images static/social static/favicons/favicon.ico` it
passes 4/4. Both directions were run locally.
- The first test asserts the source corpus exceeds 10,000 characters, so a
regression in corpus collection cannot make the orphan checks silently vacuous.
- `npx prettier --check tests/static-orphans.test.mjs` is clean.
- Dependency-free: `node:test`, `node:assert/strict`, `node:fs`, `node:path`.
No `package.json` change, no lockfile change.
- It matches on both `/img/favicon.ico` and `img/favicon.ico`, because
Docusaurus config fields accept the leading-slash-less form.
## Why there is no PR attached
The first box deletes production assets, which is outside the quality lane —
this agent opens test-only PRs and must not PR production-code changes. The
second box is red until the first lands, so shipping the guard alone would
knowingly break CI. **This needs a human or a lane that can touch production
files**; the test above is drop-in and verified, so applying both boxes together
is mechanical.
## Evidence
- Revision `00b44df`, node v26.8.2, run locally 2026-09-19.
- Sizes: `du -sh static` -> 23M; `static/images` -> 16M; `static/social` -> 148K.
- Reference check: every basename under `static/images/` and `static/social/`,
plus every `/images/...` and `/social/...` web path, grepped across `docs/`,
`blog/`, `src/`, `data/`, `adr/`, `docusaurus.config.js`, `sidebars.js` and
`static/manifest.json` -> zero hits. The only `images/` matches repo-wide are
remote `raw.githubusercontent.com/cncf/people` URLs, which resolve off-site.
- Unit coverage: `node --test --experimental-test-coverage` -> 55/55 pass; no
test enumerates `static/` or asserts the absence of orphans.
- End-to-end coverage: **unobtainable, not absent** — the repo has no
`playwright`/`cypress`/`puppeteer` in `package.json`, so no e2e claim is made
here. See #186 for the missing coverage-evidence pipeline.
## Priority
- Impact: medium-high — 70% of the published static tree is dead weight on every deploy, including a 14M video-thumbnail set from another CNCF site
- Effort: low — one deletion plus one verified test-only file, no new dependencies
---
*Filed by quality agent (hold-gated mode)*
— hive: agent=quality backend=copilot model=claude-opus-5
Activity