Docs / Admission hooks

Admission hooks

The same sizing as the CLI, at the point a job is submitted: a request that cannot hold the job is refused or warned, with the batch or card that fits.

Each block below was run on 2026-10-18; how, is said under it. Free under 8 GPUs (pricing). What each hook sends over the network: trust page.

SkyPilot admin policy

On PyPI as neurarch-skypilot 0.1.1. Standard library only; it needs the neurarch CLI (Node 18+) on the host that applies the policy, because that is what sizes a Hugging Face id offline.

$ pip install neurarch-skypilot
$ npm i -g neurarch
$ echo "admin_policy: neurarch_skypilot.NeurarchPolicy" >> ~/.sky/config.yaml

For a team, put the same admin_policy: line in the API server's config instead, with the package installed on the server. The job's facts go in the task's envs, the same variables the training script reads:

resources:
  accelerators: A10G:1
envs:
  NEURARCH_MODEL: Qwen/Qwen3-8B   # HF id, or a path to a .neurarch.json
  NEURARCH_BATCH: "8"             # per device; default 32
  NEURARCH_SEQ_LEN: "2048"
  NEURARCH_ATTENTION: sdpa        # sdpa | flash | eager
  # NEURARCH_LORA_RANK: "16"      # set for a LoRA run
  NEURARCH_HOURS: "6"             # needed for a cost cap

That task is refused before a VM is provisioned:

Neurarch: Qwen/Qwen3-8B needs 297.9 GB to train at batch 8 per device, 2048 tokens (peak 259.1 GB
x 1.15 headroom), and A10G:1 holds 24 GB. No batch fits A10G:1: the weights, gradients and optimizer
state alone need 180.9 GB. No single card holds it; the smallest request that does is A100-80GB:4,
sharded (FSDP or ZeRO-3) at the same global batch of 8. Sized offline by the neurarch CLI from the
model's structure; nothing was trained. Change `accelerators:` and resubmit.

How this was run: neurarch-skypilot 0.1.0 installed from PyPI and CLI 0.2.0 from npm, applied to this task with SkyPilot's request objects stubbed (the package's own test fakes), not through a live sky launch.

Setting (env var on the policy host)DefaultDoes
NEURARCH_POLICY_MUTATEoffRewrite a far-too-large request to the cheapest one that holds the job (single node only).
NEURARCH_POLICY_MAX_COST_USDnoneRefuse a run whose list-price cost is over the cap (needs NEURARCH_HOURS).
NEURARCH_POLICY_FAIL_CLOSEDoffRefuse instead of admitting with a warning when sizing cannot answer.
NEURARCH_POLICY_SIZINGautocli keeps sizing on the host; auto falls back to the hosted plan for a graph file.

SLURM cli_filter

On PyPI as neurarch-slurm 0.1.0. It builds on neurarch-skypilot 0.1.1, which pip installs with it, and needs the neurarch CLI (Node 18+) on every submit host.

It runs in the user's own sbatch, srun or salloc on the submit host, so slurmctld never waits on it. The cost of that choice: it tells people, it is not an enforcement boundary, because anything that submits around the client skips it. Every failure lets the submit through.

$ pip install neurarch-slurm
$ npm i -g neurarch
$ neurarch-slurm --print-lua > /etc/slurm/cli_filter.lua
$ echo 'CliFilterPlugins=cli_filter/lua' >> /etc/slurm/slurm.conf

The job's facts go in the batch script as #NEURARCH directives, read the way sbatch reads #SBATCH:

#!/bin/bash
#SBATCH --gres=gpu:a10g:1
#NEURARCH model=Qwen/Qwen3-8B batch=8 seq-len=2048 attention=sdpa
#NEURARCH hours=6

The helper the plugin calls can be run by hand, which is how this was checked:

$ neurarch-slurm --script finetune-qwen3-8b.sbatch --gres gpu:a10g:1 --nodes 1 --mode reject
decision=reject
message=Neurarch: Qwen/Qwen3-8B needs 297.9 GB to train at batch 8 per device, 2048 tokens (peak 259.1 GB x 1.15 headroom), and a10g:1 holds 24 GB. ... Change --gres and resubmit.

How this was run: the helper installed from our repository with the CLI 0.2.0 from npm, outside a SLURM cluster. In the default warn mode the same message prints with "Submitted anyway" and the job is submitted; reject refuses only a request that cannot hold the job or a run over the cost cap.

Claude Code hook

A PreToolUse hook from neurarch-mcp. Before each Bash call it reads the pending command; when the command launches training (python train.py, accelerate launch, torchrun, deepspeed, sky launch) or serving (vllm serve, python -m sglang.launch_server) and the job will not fit, it exits 2, which blocks the call and hands the agent the reason. Every other command runs untouched and the hook prints nothing.

As a plugin (the hook, the MCP server and a skill in one install):

/plugin marketplace add neurarch-ai/neurarch-mcp
/plugin install neurarch@neurarch-ai

Or by hand, in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "npx -y neurarch-mcp hook --hf" }
        ]
      }
    ]
  }
}

What the agent sees when it tries a full fine-tune of Qwen3-8B on one A10G:

$ echo '{"tool_name":"Bash","tool_input":{"command":"python train.py --model_name_or_path Qwen/Qwen3-8B --gpu A10G --per_device_train_batch_size 8"}}' | npx -y neurarch-mcp hook --hf; echo $?
neurarch: this training job will not fit A10G.
  will not fit an A10G: needs ~298 GB with headroom (peak 259 GB) at batch 8, seq 2048; no batch fits, weights and optimizer state alone are 157 GB.
  ...
  A calibrated estimate, not a measurement: at the edge of a card it is a fit check. If you are sure it fits, prefix the command with NEURARCH_ALLOW_OOM=1.
2

Run with neurarch-mcp 0.18.0 from npm; the current release is 0.19.0. --hf lets it read a config from huggingface.co (cached for a day); drop it to stay offline, and then only local models are sized. Anything the command leaves out (card, batch, sequence length) comes from the job: block of the nearest .neurarch.yml, the same block the PR bot reads.