the finger, the moon and the idle T4 a pre-flight linter for ML environment mismatches A developer built nocando, a pre-flight environment-semantics linter for ML workloads that checks whether code is coherent with the declared hardware environment before occupying resources. The tool aims to prevent wasted compute time, such as a three-hour session on a T4 GPU that sat idle because scikit-learn's SVM is CPU-only. The developer argues that the core problem is not a shortage of compute but poor allocation of existing resources. This is a submission for Weekend Challenge: Passion Edition There's a teaching in the Buddhist tradition about a finger pointing at the moon. I say "Hey, look at the moon " and point upward — and you, instead, are distracted by my finger, and respond with critique about my finger. As with all spiritual parables, the meanings to be drawn are contextual and personal. Here is mine: I built a small tool called nocando pip install nocando , and nocando is the finger. The moon is energy allocation. Let me start with the finger, since that's what the dev challenge asks for. nocando is a pre-flight environment-semantics linter for ML workloads. It answers one question before you occupy hardware: is the code you wrote coherent with the declared environment? Syntax linters know your language. Type checkers know your data shapes. Nothing knows that from sklearn.svm import SVC plus "Runtime: T4" is an incoherent sentence. Now something does. This has been fomenting, churning and brewing... passion — desire...I spent a year in machine learning chasing my tail through undocumented mismatches: the constant expectation that I should have known something that is never given to me, anywhere, in any linear order. And this week I had enough of this for all of us because I know this is not unique to me, every time I look things up, I am met with some form of "yup... " as if acknowledgement makes the externalization acceptable. It doesn't. I sat for three and a half hours on a Colab T4 at 0% utilization because, unbeknownst to me, scikit-learn for SVM is CPU-only. It has no CUDA backend. That fact is thoroughly documented, if you know to look for that. Buried in Stack threads and subreddits, indexed by symptom, findable only after you've already failed, because failure is what generates the search terms. Nothing says this up front. So I built a thing that says it up front. That T4 could have been someone else's, or simply not occupied. Now the moon. Notice what that wasted session actually was: not a worse model, not a slower result. Nothing. Heat. Eliminating it asks no one to sacrifice anything — nobody's workflow degrades, nobody is told to want less. Multiply one wasted Tuesday across every student, bootcamp cohort, and tutorial-follower hitting the same mismatch, and you get a large number composed entirely of moments nobody valued. No one watches the intersections. I do. Like a lint trap. There is no shortage of accepted scholarly literature, across a wide berth of domains, laying out in detail how much resource there actually is — energy, water, compute. Our major malfunction is not one of needing more. It is, simply, allocation. Simple, not easy — that's a recovery slogan, and the implication is exact: recovery is not complicated, but our thinking makes it so. Repent means just this: turn around, turn back from your thinking. And this is what is not easy — unsticking from a way of thinking things should be. More is better. Better is better. Or: more is simply more, and better might only be relatively so, for a few, in the short term. I'll offer one anecdote about how sticky that thinking is. In my first "big data" graduate course, my distance-learning group and I had a divorce. One mitigating factor, in my perception, was an old, uninformed bias about what makes data "big." To them, big data had to meet some n threshold — never a complexity or quality one. Contemporary definitions freely state that complexity is absolutely a characteristic that can make data big; they were unable to accept this. Quantity was legible to them. Quality was not. Ultimately, we divorced. I share this because that bias is not confined to one group project — it's the same reflex driving the industry's fraught path of more energy needs for more data centers for more data for more training for more models for more. I propose nothing novel at all. Allocation. We already use allocation in all sorts of systems, and fail to use it in all sorts of others, to our respective advantage and detriment. We do not need more. We simply need better. A linter cannot fix an industry's thinking. A finger is not the moon. But a finger that points at the exact line of your file, one second before you burn the hardware, and says this will produce heat and nothing else — here's the cheaper allocation — that's a small, tangible, working piece of the turn. bash $ nocando train.py --runtime t4 ──────────────────────────────────────────────────────────────── nocando pre-flight audit — train.py declared runtime: T4 accelerator ──────────────────────────────────────────────────────────────── VERDICT: ✗ NO CAN DO BLOCK scikit-learn line 11: clf = GridSearchCV SVC ... : scikit-learn executes on CPU only. It has no CUDA backend and will not touch a GPU regardless of runtime. Heavy CPU-bound calls found: SVC, GridSearchCV. These will run entirely on CPU while the T4 idles at ~0% utilization. ↳ NVIDIA cuML RAPIDS is a drop-in GPU replacement, or switch to a CPU runtime often faster AND free . ──────────────────────────────────────────────────────────────── It reads both directions — here it catches the opposite mismatch, plus an environment that contradicts itself: bash $ nocando environment.yml advise contradictory pin: numpy line 6: - numpy=1.26.4 and line 9: - numpy==2.1.0 : pinned to different major versions in this artifact. Whichever installs last silently wins or the resolver fails outright. And when the deterministic core hits its own honest limits, it can hand what it learned to a language model — clearly labeled as advisory: bash $ nocando frankenstein project.py --runtime t4 --agent ...deterministic report first, including: unscanned recognized, no rule exists yet : some obscure lab package — absence of a warning above means 'no rule fired,' not 'verified compatible.' ════════════════════════════════════════════════════════════════ ADVISORY Gemini — NOT part of the deterministic verdict above. ════════════════════════════════════════════════════════════════ ...Gemini answers only the open questions the core refused to guess at, instructed to mark its guesses as guesses. Try it yourself in ten seconds — no launch, no deploy, nothing to configure. pip install nocando , then point it at any script, notebook, environment.yml , or even a PDF assignment handout you already have. It reads your file, prints a report, exits. Exit codes 0/1/2 drop straight into CI and pre-commit hooks. A pre-flight environment-semantics linter for ML workloads. It answers one question before you occupy hardware — in either direction: Is the code you wrote coherent with the environment you declared? That covers CPU-only workloads parked on GPUs waste , unconditional .cuda calls headed for a CPU runtime crash at minute 40 , CUDA-requiring libraries on Apple Silicon import failure , and wrong-generation hardware flash-attn needs compute capability = 8.0; a Colab T4 is 7.5 . The unit of analysis is the mismatch . Deliberate slow-on-CPU choices are not flagged — can't legislate morality. Syntax linters know your language. Type checkers know your data shapes Nothing knows that from sklearn.svm import SVC plus "Runtime: T4" is an incoherent sentence — the constraint lives at the intersection of library × hardware × platform, and nobody owns intersections. nocando owns this one. Zero LLM calls, zero telemetry, pure static analysis. Certified by Ted stoop tabby, QA . PyPI: https://pypi.org/project/nocando/ https://pypi.org/project/nocando/ Pure-Python static analysis — deliberately zero LLM calls, zero telemetry, stdlib only in the core. A pre-flight check you can't trust Three audit dimensions, each born from a real failure of mine: Runtime coherence — CPU-only workloads pointed at GPUs; unconditional .cuda calls headed for CPU runtimes which crash at minute 40, right after the data prep you already waited through ; and hardware-generation traps like flash-attn on a T4 needs compute capability ≥ 8.0; a T4 is 7.5 — a constraint that lives nowhere a beginner would ever look . Environment-manager coherence — conda + pip install-order corruption conda's own docs bury "conda first, pip last" three paragraphs deep in a blog post nobody reads until their environment is already dead , and the notebook pip vs %pip trap, where an install "succeeds" into a different Python than your kernel. Ingestion honesty — for Frankenstein code mixing libraries it has never seen together, nocando explicitly reports every recognized-but-unscanned library, so silence is never mistaken for "verified compatible." It flags self-contradictory version pins because that check is pure logic and can never go stale — the tool refuses to hardcode a compatibility phonebook that would start decaying the day it was written. It reads Python, R Colab serves both kernels — same trap, different tongue , notebooks, conda manifests, and prose: feed it the assignment PDF before any code exists and it audits the workload the document is designed to produce. Every finding names its location and quotes the offending line — line 11: ... for scripts, cell 3: ... for notebooks, because Colab shows you cells, not line counts. 49 tests, and the fixtures are real: my actual graduate coursework, fed through cold. One of those specimens revealed that the tool itself had shipped with an unexamined assumption — "ML means Python" — until my own R coursework caught my own linter doing the exact thing it was built to detect. That finding is preserved in the repo's lineage docs on purpose. Nobody is exempt from the audit, including the auditor. But a deterministic core has a horizon, and the tool is honest about where it sits: "support vector machine" written in words instead of SVC , an unrecognized lab package nobody has mapped, a training task described entirely in prose. The core refuses to guess at these — so I built the layer where guessing is allowed to live, clearly labeled. nocando --handoff packages the full report plus its open questions into a prompt you can paste into any assistant. nocando --agent goes one step further: it sends that same prompt to Gemini in a single API call and prints the response under an ADVISORY banner — commentary on the report, never part of it. The invariant is tested, not aspirational: neither flag can alter a finding, a verdict, or an exit code. Gemini answers only the questions the deterministic core explicitly could not, and is instructed to mark its guesses as guesses. That's what I think meaningful AI integration looks like: not a model in the loop, but a model at the boundary — consulted exactly where determinism ends, and not one line before. Nothing is good or bad, it is our relationship with it. Probabilistic AI can be awesome. The right tool for the right job, in the right way. It's like going to the hardware store for bread. Making this tool covers a lot of unknowable ground. It can't be a look up table. Updates, and software versions change every day. The entire computing landscape is constantly in flux. If this were purely agentic it would be an energy suck. The question is/was; where in here should determinism and agentic forces best interweave so that like, any relationship, they work better together. Certified by Ted stoop tabby, QA . -- < nocando's --agent flag sends the deterministic audit's unresolved questions to Gemini google-genai, one generate content call, model configurable via GEMINI MODEL . The integration is deliberately narrow: the deterministic report is the authority; Gemini is the consultant for the ambiguities the core refuses to guess at — unscanned libraries, framework-unconfirmed prose, allocation alternatives. Advisory only, invariant-tested, degrades gracefully without a key.--