diff --git a/.github/workflows/no-vanity-metrics.yml b/.github/workflows/no-vanity-metrics.yml index d156f331a..43e35cd52 100644 --- a/.github/workflows/no-vanity-metrics.yml +++ b/.github/workflows/no-vanity-metrics.yml @@ -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 + diff --git a/jouleclaw/jouleclaw-rs/crates/jouleclaw-jcp/src/lib.rs b/jouleclaw/jouleclaw-rs/crates/jouleclaw-jcp/src/lib.rs index 02ff2f534..762e7fb03 100644 --- a/jouleclaw/jouleclaw-rs/crates/jouleclaw-jcp/src/lib.rs +++ b/jouleclaw/jouleclaw-rs/crates/jouleclaw-jcp/src/lib.rs @@ -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 diff --git a/jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/runtime/energy.rs b/jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/runtime/energy.rs index 5c5976f52..51672402b 100644 --- a/jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/runtime/energy.rs +++ b/jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/src/runtime/energy.rs @@ -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}; diff --git a/scripts/check-orphan-sources.py b/scripts/check-orphan-sources.py new file mode 100755 index 000000000..49143bf55 --- /dev/null +++ b/scripts/check-orphan-sources.py @@ -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())