check: no library source file is unreachable from its crate root #142
4 changed files with 250 additions and 4 deletions
43
.github/workflows/no-vanity-metrics.yml
vendored
43
.github/workflows/no-vanity-metrics.yml
vendored
|
|
@ -140,3 +140,46 @@ jobs:
|
|||
exit 1
|
||||
|
||||
# (touched to test that this very change supersedes its own queued runs)
|
||||
# ── No library source file is unreachable from its crate root ─────────────
|
||||
#
|
||||
# An orphan `.rs` — one no `mod`, `#[path]` or `include!` ever names — is not
|
||||
# merely untested. It is never compiled, so it is invisible to every check in
|
||||
# this repository at once: cargo check, cargo test, clippy, the conformance
|
||||
# suites. It cannot fail, because nothing asks it anything.
|
||||
#
|
||||
# This repo keeps finding that family: a feature-gated test run without
|
||||
# `--features`, a platform-gated meter pinned to one runner, a conformance
|
||||
# suite in `main()` that `cargo test` compiles and never runs. An orphan file
|
||||
# is the strongest member — it does not even type-check.
|
||||
#
|
||||
# The hazard is the next reader, not the dead code. One such file documents
|
||||
# itself as measuring energy via IOReport and in fact multiplies CPU seconds
|
||||
# by a fixed watt figure; a reader who trusts that header gets a fabricated
|
||||
# number. The script's baseline pins today's 25 so this does not go red on a
|
||||
# pre-existing condition, and so the count can only ratchet down.
|
||||
orphan-sources:
|
||||
name: No new orphan source files
|
||||
runs-on: linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Both directions, before trusting the result: a planted orphan must fail
|
||||
# the check, and removing it must pass. A guard verified in only the
|
||||
# passing direction is a guard that has never been seen to work.
|
||||
- name: The check fails closed (self-test)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
probe=jcp/jcp-rs/crates/jcp-efficiency/src/_ci_orphan_probe.rs
|
||||
printf 'pub fn never_compiled() -> u64 { 0 }\n' > "$probe"
|
||||
if python3 scripts/check-orphan-sources.py >/dev/null 2>&1; then
|
||||
rm -f "$probe"
|
||||
echo "::error::the orphan check passed with a planted orphan present"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$probe"
|
||||
python3 scripts/check-orphan-sources.py
|
||||
|
||||
- name: Run the orphan-source check
|
||||
run: python3 scripts/check-orphan-sources.py
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ use jcp_schema::{ContentId, Did, Energy, Frame, FrameKind, SignedGrant, Trust};
|
|||
use jouleclaw_mcp::{McpError, MeteredTool};
|
||||
use jouleclaw_transparency_log::{InclusionProof, LogError, MerkleTree, SignedTreeHead, TreeHead};
|
||||
|
||||
pub mod skill;
|
||||
pub mod stack;
|
||||
|
||||
/// The JCP resource of an MCP tool: the content address of its stable
|
||||
/// `tool_id` (e.g. `"mcp:filesystem/read"`). A grant scoped to this resource
|
||||
/// authorizes exactly this tool — a same-named tool on another server is a
|
||||
|
|
|
|||
|
|
@ -1,8 +1,33 @@
|
|||
//! Energy metering for Apple Silicon via IOReport.
|
||||
//! Energy **estimation** from process time. Not a measurement, and not
|
||||
//! IOReport — despite what this header said until 2026-10-02.
|
||||
//!
|
||||
//! Measures actual GPU, CPU, ANE, and DRAM energy consumption in joules
|
||||
//! using Apple's IOReport framework. Falls back to time-based estimation
|
||||
//! on non-macOS platforms.
|
||||
//! # What it actually does
|
||||
//!
|
||||
//! This file is **not compiled**: `runtime/mod.rs` does not declare it, so no
|
||||
//! `cargo check`, `cargo test` or clippy run in this repository has ever looked
|
||||
//! at it. The corrected description below is therefore a warning to the next
|
||||
//! reader rather than a description of running code.
|
||||
//!
|
||||
//! It does not call IOReport. The macOS path reads `getrusage` and multiplies
|
||||
//! CPU seconds by a fixed watt figure; the other path multiplies wall-clock by
|
||||
//! a different fixed figure. Each then splits the result across GPU, CPU, ANE
|
||||
//! and DRAM by constant fractions.
|
||||
//!
|
||||
//! The per-subsystem fields are the sharper problem. `gpu_joules`,
|
||||
//! `cpu_joules`, `ane_joules` and `dram_joules` read as four independent
|
||||
//! observations; they are four fractions of one guess. A split implies
|
||||
//! resolution that does not exist, which invites per-subsystem conclusions
|
||||
//! nothing here could support — precision standing in for measurement.
|
||||
//!
|
||||
//! # Before wiring this in
|
||||
//!
|
||||
//! A real meter lives in this same workspace: `jouleclaw-energy`, with a
|
||||
//! per-rail liveness gate that declines to report rather than report a frozen
|
||||
//! counter. Any figure intended to reach a receipt must come from there. If a
|
||||
//! coarse power figure is genuinely wanted for scheduling, that is a legitimate
|
||||
//! need — but it is `Estimator`-class in the JCP metering ladder (SPEC §5.1),
|
||||
//! it MUST NOT be presented as `ModelBased`, and the subsystem split MUST NOT
|
||||
//! be presented at all.
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
|
|
|
|||
175
scripts/check-orphan-sources.py
Executable file
175
scripts/check-orphan-sources.py
Executable file
|
|
@ -0,0 +1,175 @@
|
|||
#!/usr/bin/env python3
|
||||
"""No library source file is unreachable from its crate root.
|
||||
|
||||
An orphan `.rs` file — one no `mod` declaration, `#[path]` attribute or
|
||||
`include!` ever names — is not merely untested. It is **never compiled**, so it
|
||||
is invisible to every check in this repository at once: not `cargo check`, not
|
||||
`cargo test`, not clippy, not the conformance suites. It cannot fail, because
|
||||
nothing ever asks it anything.
|
||||
|
||||
That makes it the strongest member of a family this repo keeps finding: a
|
||||
feature-gated test run without `--features`, a platform-gated meter on a
|
||||
single-platform runner, a conformance suite living in `main()` that
|
||||
`cargo test` compiles and never runs. In each the code looks present and
|
||||
reports nothing. An orphan file does not even type-check.
|
||||
|
||||
The hazard is not the dead code itself, it is the next reader. A file that
|
||||
documents itself as doing something real, sitting in the tree beside files that
|
||||
do, will eventually be wired in by someone who trusts its header — and it has
|
||||
never been compiled, let alone verified.
|
||||
|
||||
What is NOT an orphan, and why the exclusions matter:
|
||||
|
||||
src/bin/*.rs cargo auto-discovers these as binaries
|
||||
examples/, tests/, benches/ likewise auto-discovered
|
||||
lib.rs, main.rs, mod.rs, build.rs entry points
|
||||
foo.rs beside a foo/ directory a directory module of the same name
|
||||
|
||||
A first pass at this check counted 77 orphans by missing the `src/bin`
|
||||
exclusion. The real figure is a quarter of that and sits in one crate. A
|
||||
detector that over-reports is as useless as one that under-reports, so the
|
||||
exclusions are part of the check, not a convenience.
|
||||
|
||||
Usage:
|
||||
scripts/check-orphan-sources.py # fail on anything not in BASELINE
|
||||
scripts/check-orphan-sources.py --list # print every orphan found
|
||||
|
||||
The baseline pins what exists today so CI does not go red on a pre-existing
|
||||
condition, and so the number can only go **down**. Removing a file from the
|
||||
baseline is the point; adding one is a regression and should be argued for in a
|
||||
commit message, not slipped in.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Pre-existing orphans, pinned 2026-10-02. All 25 are in jouleclaw-omni, which
|
||||
# also holds `runtime/energy.rs` — a file documenting itself as measuring
|
||||
# "actual GPU, CPU, ANE and DRAM energy consumption using Apple's IOReport
|
||||
# framework" while in fact multiplying `getrusage` CPU seconds by a fixed 15 W
|
||||
# and apportioning the result 60/20/20 across subsystems. It has never been
|
||||
# compiled. That is precisely the trap this check exists to stop growing:
|
||||
# a real meter with a per-rail liveness gate lives in `jouleclaw-energy`, and a
|
||||
# reader who wires this one in instead gets a fabricated figure.
|
||||
BASELINE = {
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/bark.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/canary.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/chatterbox.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/depth_anything.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/esrgan.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/fibo.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/jamba.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/kokoro.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/mamba2.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/mimo.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/mochi.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/moonshine.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/nemotron.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/parler_tts.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/sam2.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/sana_wm_gpu_dispatchers_draft.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/sd3.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/architecture/sesame_csm.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/expert_cache.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/fusion.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/gpv.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/inference/video_decode.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/runtime/energy.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/server/model_library.rs",
|
||||
"jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/server/worker.rs",
|
||||
}
|
||||
|
||||
ENTRY = {"lib", "main", "mod", "build"}
|
||||
|
||||
DECL = re.compile(r"(?m)^\s*(?:pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_]\w*)\s*;")
|
||||
PATH_ATTR = re.compile(r'#\[path\s*=\s*"([^"]+)"\]')
|
||||
INCLUDE = re.compile(r'include!\s*\(\s*"([^"]+)"')
|
||||
|
||||
|
||||
def declared_names(src_root: str) -> set[str]:
|
||||
"""Every module name the crate's own sources name, by any mechanism."""
|
||||
names: set[str] = set()
|
||||
for dirpath, dirs, files in os.walk(src_root):
|
||||
dirs[:] = [d for d in dirs if d != "target"]
|
||||
for f in files:
|
||||
if not f.endswith(".rs"):
|
||||
continue
|
||||
try:
|
||||
text = open(os.path.join(dirpath, f), encoding="utf-8", errors="ignore").read()
|
||||
except OSError:
|
||||
continue
|
||||
names.update(DECL.findall(text))
|
||||
for pat in (PATH_ATTR, INCLUDE):
|
||||
for hit in pat.findall(text):
|
||||
names.add(os.path.basename(hit).removesuffix(".rs"))
|
||||
return names
|
||||
|
||||
|
||||
def find_orphans(repo: str) -> list[str]:
|
||||
orphans: list[str] = []
|
||||
seen_roots: dict[str, set[str]] = {}
|
||||
for dirpath, dirs, files in os.walk(repo):
|
||||
dirs[:] = [d for d in dirs if d not in ("target", ".git", "node_modules")]
|
||||
rel = os.path.relpath(dirpath, repo)
|
||||
if "/src" not in "/" + rel + "/":
|
||||
continue
|
||||
# cargo auto-discovers these; they are reachable without a `mod`.
|
||||
if "/src/bin" in "/" + rel or rel.endswith("/src/bin"):
|
||||
continue
|
||||
rs = [f for f in files if f.endswith(".rs")]
|
||||
if not rs:
|
||||
continue
|
||||
src_root = os.path.join(repo, rel.split("/src")[0], "src")
|
||||
if src_root not in seen_roots:
|
||||
seen_roots[src_root] = declared_names(src_root)
|
||||
declared = seen_roots[src_root]
|
||||
for f in rs:
|
||||
stem = f[:-3]
|
||||
if stem in ENTRY:
|
||||
continue
|
||||
if stem in declared:
|
||||
continue
|
||||
if os.path.isdir(os.path.join(dirpath, stem)):
|
||||
continue # a directory module of the same name
|
||||
orphans.append(os.path.normpath(os.path.join(rel, f)))
|
||||
return sorted(orphans)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
repo = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
orphans = find_orphans(repo)
|
||||
|
||||
if "--list" in sys.argv:
|
||||
for o in orphans:
|
||||
mark = " (baseline)" if o in BASELINE else " NEW"
|
||||
print(f"{o}{mark}")
|
||||
print(f"\n{len(orphans)} orphan(s); {len(BASELINE)} pinned in the baseline")
|
||||
return 0
|
||||
|
||||
new = [o for o in orphans if o not in BASELINE]
|
||||
gone = sorted(BASELINE - set(orphans))
|
||||
|
||||
if gone:
|
||||
print("These baseline orphans are reachable again, or were removed — good.")
|
||||
print("Drop them from BASELINE in this script so the floor ratchets down:")
|
||||
for g in gone:
|
||||
print(f" {g}")
|
||||
print()
|
||||
|
||||
if new:
|
||||
print("New orphan source file(s): never compiled, so invisible to every")
|
||||
print("check in this repo — cargo check, cargo test, clippy, conformance.")
|
||||
print("Declare it with `mod`, move it under src/bin/, or delete it.\n")
|
||||
for n in new:
|
||||
print(f" {n}")
|
||||
return 1
|
||||
|
||||
print(f"No new orphan sources ({len(orphans)} pinned, {len(BASELINE)} expected).")
|
||||
return 1 if gone else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Reference in a new issue