Skill Tracer
Skills › Scientific Agent Skills › get-available-resources › resource_semantics.md

resource_semantics.md

234 lines · skills/get-available-resources/references/resource_semantics.md at 5f2bc03

Open on GitHub
1# Resource Semantics
2
3Research and behavior cut-off: **2026-10-01**. See
4[`sources.md`](sources.md) for the official documentation used.
5

Resource Semantics

Research and behavior cut-off: 2026-10-01. See [sources.md](sources.md) for the official documentation used.

Raw lines 1 to 5
6## Core rule: inventory is not entitlement
7
8Never treat a host-wide count as a promise that the current process can use it.
9Interpret usable resources as the intersection of independently observed
10constraints:
121. host inventory;
132. process affinity or processor-group scope;
143. cgroup/container constraints;
154. scheduler allocation;
165. accelerator visibility and device permissions; and
176. application runtime compatibility.
19Missing evidence means **unknown**, not unlimited. Scheduler variables can
20describe an allocation without proving that affinity or cgroup enforcement is
21enabled. Conversely, a cgroup or affinity mask can be stricter than the
22scheduler request.

Core rule: inventory is not entitlement

Never treat a host-wide count as a promise that the current process can use it. Interpret usable resources as the intersection of independently observed constraints:

  1. host inventory;
  2. process affinity or processor-group scope;
  3. cgroup/container constraints;
  4. scheduler allocation;
  5. accelerator visibility and device permissions; and
  6. application runtime compatibility.

Missing evidence means unknown, not unlimited. Scheduler variables can describe an allocation without proving that affinity or cgroup enforcement is enabled. Conversely, a cgroup or affinity mask can be stricter than the scheduler request.

Raw lines 6 to 23
26### Physical and logical CPUs
28- A logical CPU is an operating-system scheduling unit. Simultaneous
29 multithreading can expose multiple logical CPUs on one physical core.
30- A physical-core count describes topology, not the number of independent
31 workers the process may start.
32- `psutil.cpu_count(logical=True)`, macOS `hw.logicalcpu`, and POSIX
33 `SC_NPROCESSORS_ONLN` provide host/system inventory. They can exceed the
34 CPUs usable by the process. `os.cpu_count()` is a fallback: Python 3.13+
35 `-X cpu_count` or `PYTHON_CPU_COUNT` overrides both Python CPU-count APIs.
36 The detector prefers independent inventory and keeps Python process count
37 as a separate planning input.
38- `os.process_cpu_count()` (Python 3.13+) is process-aware. On supported
39 platforms, affinity APIs provide a more explicit process constraint.
40- On Windows systems with multiple processor groups, a system-wide logical
41 count and one process/thread group's usable count can differ. Windows 11
42 and Server 2022 default affinity spans groups; do not assume every modern
43 Windows process is capped at 64 logical CPUs.
45The detector reports host logical and physical counts separately. It never
46derives physical cores from a logical count.

Physical and logical CPUs

  • A logical CPU is an operating-system scheduling unit. Simultaneous multithreading can expose multiple logical CPUs on one physical core.
  • A physical-core count describes topology, not the number of independent workers the process may start.
  • psutil.cpu_count(logical=True), macOS hw.logicalcpu, and POSIX SC_NPROCESSORS_ONLN provide host/system inventory. They can exceed the CPUs usable by the process. os.cpu_count() is a fallback: Python 3.13+ -X cpu_count or PYTHON_CPU_COUNT overrides both Python CPU-count APIs. The detector prefers independent inventory and keeps Python process count as a separate planning input.
  • os.process_cpu_count() (Python 3.13+) is process-aware. On supported platforms, affinity APIs provide a more explicit process constraint.
  • On Windows systems with multiple processor groups, a system-wide logical count and one process/thread group's usable count can differ. Windows 11 and Server 2022 default affinity spans groups; do not assume every modern Windows process is capped at 64 logical CPUs.

The detector reports host logical and physical counts separately. It never derives physical cores from a logical count.

Raw lines 26 to 47
48### Affinity, cpusets, and quotas
50- Process affinity limits the logical CPUs on which a process may execute.
51- Linux `cpuset.cpus.effective` reports CPUs actually granted after parent
52 constraints. The requested `cpuset.cpus` can differ.
53- `/proc/self/status` exposes `Cpus_allowed_list`, but the detector prefers
54 affinity APIs and cgroup effective cpusets.
55- cgroup v2 `cpu.max` is `$MAX $PERIOD`. `max` means no local bandwidth limit.
56 A finite ratio is CPU-time capacity, possibly fractional; it is not a core
57 topology count.
58- Parent cgroups also constrain children, so the detector takes the most
59 restrictive finite visible ancestor quota. Membership is resolved through
60 `/proc/self/mountinfo` cgroup2 root/mountpoint fields. A namespace or delegated
61 mount may hide stronger ancestors. Missing files are not evidence of
62 unlimited capacity; cgroup v1 is outside the collector's implemented scope.
64For a `cpu.max` ratio of 1.5, the snapshot reports
65`capacity_cores: 1.5` and a conservative CPU-bound worker ceiling of 1. A
66workload may choose more threads for latency hiding, but it should expect
67throttling and must not describe those threads as 1.5 physical cores.

Affinity, cpusets, and quotas

  • Process affinity limits the logical CPUs on which a process may execute.
  • Linux cpuset.cpus.effective reports CPUs actually granted after parent constraints. The requested cpuset.cpus can differ.
  • /proc/self/status exposes Cpus_allowed_list, but the detector prefers affinity APIs and cgroup effective cpusets.
  • cgroup v2 cpu.max is $MAX $PERIOD. max means no local bandwidth limit. A finite ratio is CPU-time capacity, possibly fractional; it is not a core topology count.
  • Parent cgroups also constrain children, so the detector takes the most restrictive finite visible ancestor quota. Membership is resolved through /proc/self/mountinfo cgroup2 root/mountpoint fields. A namespace or delegated mount may hide stronger ancestors. Missing files are not evidence of unlimited capacity; cgroup v1 is outside the collector's implemented scope.

For a cpu.max ratio of 1.5, the snapshot reports capacity_cores: 1.5 and a conservative CPU-bound worker ceiling of 1. A workload may choose more threads for latency hiding, but it should expect throttling and must not describe those threads as 1.5 physical cores.

Raw lines 48 to 68
69### Python worker pools
71- Current Python `multiprocessing.Pool` and `ProcessPoolExecutor` defaults use
72 `os.process_cpu_count()` when available.
73- Python 3.14 no longer uses `fork` as the default start method on any platform.
74 Code that depends on a particular start method must request it deliberately.
75- Process workers do not make each worker's native-library threads disappear.
76 BLAS/OpenMP threads multiplied by process workers commonly oversubscribe an
77 allocation.
78- Windows `ProcessPoolExecutor` has a documented maximum of 61 workers.
80Use the workload planner's worker and threads-per-worker values as conservative
81ceilings, then benchmark a real representative workload. Do not run a synthetic
82stress test merely to discover capacity.

Python worker pools

  • Current Python multiprocessing.Pool and ProcessPoolExecutor defaults use os.process_cpu_count() when available.
  • Python 3.14 no longer uses fork as the default start method on any platform. Code that depends on a particular start method must request it deliberately.
  • Process workers do not make each worker's native-library threads disappear. BLAS/OpenMP threads multiplied by process workers commonly oversubscribe an allocation.
  • Windows ProcessPoolExecutor has a documented maximum of 61 workers.

Use the workload planner's worker and threads-per-worker values as conservative ceilings, then benchmark a real representative workload. Do not run a synthetic stress test merely to discover capacity.

Raw lines 69 to 83
84## Memory
86### Host and effective memory
88- `psutil.virtual_memory().total` and `.available` describe system-visible
89 memory, not necessarily the process limit.
90- Linux `/proc/meminfo` `MemAvailable` is the standard-library fallback for a
91 host-availability estimate.
92- cgroup v2 `memory.current` is current cgroup usage.
93- `memory.high` is a throttle/reclaim pressure boundary. Exceeding it does not
94 itself invoke the cgroup OOM killer, and the value can be breached.
95- `memory.max` is the hard cgroup limit. If usage cannot be reduced at that
96 boundary, the cgroup OOM killer can run.
97- Parent memory limits are hierarchical. Shared ancestor usage can reduce what
98 remains for a child, so effective remaining memory is the minimum finite
99 `limit - current` observed through the ancestor chain.
100- Slurm memory variables describe requested/allocated memory, but strict
101 enforcement depends on site configuration.
103The detector keeps host total/available values, cgroup values, scheduler values,
104and the conservative effective values distinct. A point-in-time "available"
105value can change immediately and is not a reservation.

Host and effective memory

  • psutil.virtual_memory().total and .available describe system-visible memory, not necessarily the process limit.
  • Linux /proc/meminfo MemAvailable is the standard-library fallback for a host-availability estimate.
  • cgroup v2 memory.current is current cgroup usage.
  • memory.high is a throttle/reclaim pressure boundary. Exceeding it does not itself invoke the cgroup OOM killer, and the value can be breached.
  • memory.max is the hard cgroup limit. If usage cannot be reduced at that boundary, the cgroup OOM killer can run.
  • Parent memory limits are hierarchical. Shared ancestor usage can reduce what remains for a child, so effective remaining memory is the minimum finite limit - current observed through the ancestor chain.
  • Slurm memory variables describe requested/allocated memory, but strict enforcement depends on site configuration.

The detector keeps host total/available values, cgroup values, scheduler values, and the conservative effective values distinct. A point-in-time "available" value can change immediately and is not a reservation.

Raw lines 86 to 106
107### macOS unified memory
109On Apple silicon, CPU and integrated GPU share unified memory. Do not add a
110fictional GPU VRAM amount to system RAM. The snapshot marks the memory model as
111`unified_cpu_gpu` and leaves dedicated GPU memory null. Metal framework support
112and model/operator support still require application-specific checks.

macOS unified memory

On Apple silicon, CPU and integrated GPU share unified memory. Do not add a fictional GPU VRAM amount to system RAM. The snapshot marks the memory model as unified_cpu_gpu and leaves dedicated GPU memory null. Metal framework support and model/operator support still require application-specific checks.

Raw lines 107 to 113
114## Accelerators
116Accelerator usability has separate layers:
1181. **Hardware or management visibility** — a management query returns a device.
1192. **Allocation visibility** — scheduler and named visibility variables permit
120 a device or a subset.
1213. **Device permission** — the process/container can open the required device
122 interfaces.
1234. **Driver/runtime compatibility** — driver, CUDA/ROCm/Metal runtime, and
124 framework versions are compatible.
1255. **Workload compatibility** — the requested operation and data type are
126 implemented on that backend.
128`nvidia-smi` success confirms NVIDIA management visibility only. It does not
129prove that CUDA libraries exist or are compatible. NVIDIA documents driver and
130runtime compatibility as a separate requirement.
132AMD SMI or ROCm SMI success likewise does not prove HIP/ROCm runtime usability.
133New deployments should prefer `amd-smi`; `rocm-smi` is retained as a read-only
134fallback. The fixed AMD query selects `--asic --vram`, avoiding unrelated
135static fields. Current JSON nests VRAM size as `{value, unit}`; legacy ROCm SMI
136uses a flat byte field. AMD SMI labels binary MiB values `MB`, confirmed in its
137source conversion. The detector exposes this as `reported_total_bytes` and
138leaves dedicated memory null because APU/shared-memory topology is unresolved.
139On Linux, AMD recommends `ROCR_VISIBLE_DEVICES`; on Windows it recommends
140`HIP_VISIBLE_DEVICES`.
142An Apple integrated GPU is a Metal candidate, not a CUDA GPU. AMD SMI records
143are ROCm candidates; macOS display records are Metal candidates regardless of
144hardware vendor. Neural engines, TPUs, FPGAs, and other accelerators
145must also remain distinct from CUDA devices if another inventory source adds
146them.
148The detector reads only these accelerator variable names and redacts their
149values:
151- `NVIDIA_VISIBLE_DEVICES`
152- `CUDA_VISIBLE_DEVICES`
153- `ROCR_VISIBLE_DEVICES`
154- `HIP_VISIBLE_DEVICES`
156Counts derived from those values are only upper bounds. Environment variables
157are not a security boundary and can be reset by an application; device
158namespace/cgroup controls are stronger isolation.
160Management counts describe records in a particular query. A physical NVIDIA
161GPU can expose several MIG devices, and AMD partition modes can also change
162runtime enumeration. Use the workload framework in its actual allocation to
163count usable devices; do not infer it from these candidate counts.

Accelerators

Accelerator usability has separate layers:

  1. Hardware or management visibility — a management query returns a device.
  2. Allocation visibility — scheduler and named visibility variables permit a device or a subset.
  3. Device permission — the process/container can open the required device interfaces.
  4. Driver/runtime compatibility — driver, CUDA/ROCm/Metal runtime, and framework versions are compatible.
  5. Workload compatibility — the requested operation and data type are implemented on that backend.

nvidia-smi success confirms NVIDIA management visibility only. It does not prove that CUDA libraries exist or are compatible. NVIDIA documents driver and runtime compatibility as a separate requirement.

AMD SMI or ROCm SMI success likewise does not prove HIP/ROCm runtime usability. New deployments should prefer amd-smi; rocm-smi is retained as a read-only fallback. The fixed AMD query selects --asic --vram, avoiding unrelated static fields. Current JSON nests VRAM size as {value, unit}; legacy ROCm SMI uses a flat byte field. AMD SMI labels binary MiB values MB, confirmed in its source conversion. The detector exposes this as reported_total_bytes and leaves dedicated memory null because APU/shared-memory topology is unresolved. On Linux, AMD recommends ROCR_VISIBLE_DEVICES; on Windows it recommends HIP_VISIBLE_DEVICES.

An Apple integrated GPU is a Metal candidate, not a CUDA GPU. AMD SMI records are ROCm candidates; macOS display records are Metal candidates regardless of hardware vendor. Neural engines, TPUs, FPGAs, and other accelerators must also remain distinct from CUDA devices if another inventory source adds them.

The detector reads only these accelerator variable names and redacts their values:

  • NVIDIA_VISIBLE_DEVICES
  • CUDA_VISIBLE_DEVICES
  • ROCR_VISIBLE_DEVICES
  • HIP_VISIBLE_DEVICES

Counts derived from those values are only upper bounds. Environment variables are not a security boundary and can be reset by an application; device namespace/cgroup controls are stronger isolation.

Management counts describe records in a particular query. A physical NVIDIA GPU can expose several MIG devices, and AMD partition modes can also change runtime enumeration. Use the workload framework in its actual allocation to count usable devices; do not infer it from these candidate counts.

Raw lines 114 to 164
165## Slurm and other schedulers
167The detector allowlists named Slurm variables and never dumps the environment.
168It records field names, parsed bounded counts, and memory quantities, but not
169job IDs, GPU UUIDs, node names, submit hosts, or paths.
171Important scopes:
173- `SLURM_CPUS_PER_TASK`: requested CPUs per task; suitable as a per-process
174 upper bound for one task process.
175- `SLURM_CPUS_ON_NODE`: CPUs allocated to the current batch step on the node;
176 it can be shared among tasks.
177- `SLURM_JOB_CPUS_PER_NODE`: a per-node allocation list, not a process count.
178- `SLURM_TASKS_PER_NODE`: compressed counts such as `1(x2),4`; resolve the
179 current entry with `SLURM_NODEID`. Without that index, only a uniform list
180 is interpretable. Never substitute the first node's task count. The node ID
181 itself is not emitted. Requested `SLURM_NTASKS_PER_NODE` is a fallback only
182 when the allocation list is absent. Step overrides and heterogeneous job
183 components still require application/site-specific interpretation.
184- `SLURM_MEM_PER_CPU`: memory per allocated CPU. It becomes a per-task bound
185 only when CPUs per task is known.
186- `SLURM_MEM_PER_NODE`: shared per-node memory upper bound.
187- `SLURM_GPUS_PER_TASK`: requested GPUs per task.
188- `SLURM_GPUS_ON_NODE`: GPUs allocated to the batch step on the node.
190Slurm CPU confinement requires site configuration such as task affinity or
191`task/cgroup` with core constraints. Memory requests are not strictly enforced
192unless the site enables an enforcement mechanism. Use affinity and cgroup
193observations as enforcement evidence; do not trust visible node inventory.
195For other schedulers, use their documented allocation API or variables and the
196same rule: allocation metadata and kernel enforcement are different facts.
197Do not guess from generic environment names.

Slurm and other schedulers

The detector allowlists named Slurm variables and never dumps the environment. It records field names, parsed bounded counts, and memory quantities, but not job IDs, GPU UUIDs, node names, submit hosts, or paths.

Important scopes:

  • SLURM_CPUS_PER_TASK: requested CPUs per task; suitable as a per-process upper bound for one task process.
  • SLURM_CPUS_ON_NODE: CPUs allocated to the current batch step on the node; it can be shared among tasks.
  • SLURM_JOB_CPUS_PER_NODE: a per-node allocation list, not a process count.
  • SLURM_TASKS_PER_NODE: compressed counts such as 1(x2),4; resolve the current entry with SLURM_NODEID. Without that index, only a uniform list is interpretable. Never substitute the first node's task count. The node ID itself is not emitted. Requested SLURM_NTASKS_PER_NODE is a fallback only when the allocation list is absent. Step overrides and heterogeneous job components still require application/site-specific interpretation.
  • SLURM_MEM_PER_CPU: memory per allocated CPU. It becomes a per-task bound only when CPUs per task is known.
  • SLURM_MEM_PER_NODE: shared per-node memory upper bound.
  • SLURM_GPUS_PER_TASK: requested GPUs per task.
  • SLURM_GPUS_ON_NODE: GPUs allocated to the batch step on the node.

Slurm CPU confinement requires site configuration such as task affinity or task/cgroup with core constraints. Memory requests are not strictly enforced unless the site enables an enforcement mechanism. Use affinity and cgroup observations as enforcement evidence; do not trust visible node inventory.

For other schedulers, use their documented allocation API or variables and the same rule: allocation metadata and kernel enforcement are different facts. Do not guess from generic environment names.

Raw lines 165 to 198
199## Containers and OCI
201Docker containers have no CPU or memory limit by default. When configured,
202Docker maps CPU and memory flags to cgroup controls. OCI runtime configuration
203also defines CPU, memory, and device constraints.
205Inside a container:
207- host inventory may remain visible;
208- a CPU quota can be smaller than the visible CPU set;
209- a cpuset can be smaller than the quota's apparent capacity;
210- cgroup memory can be smaller than host RAM;
211- GPU management tools can see a different set from the application runtime;
212 and
213- mounted-volume capacity can differ from writable quota.
215Always report observed cgroup controls and uncertainty. A container marker
216without a finite cgroup value does not imply a finite limit.

Containers and OCI

Docker containers have no CPU or memory limit by default. When configured, Docker maps CPU and memory flags to cgroup controls. OCI runtime configuration also defines CPU, memory, and device constraints.

Inside a container:

  • host inventory may remain visible;
  • a CPU quota can be smaller than the visible CPU set;
  • a cpuset can be smaller than the quota's apparent capacity;
  • cgroup memory can be smaller than host RAM;
  • GPU management tools can see a different set from the application runtime; and
  • mounted-volume capacity can differ from writable quota.

Always report observed cgroup controls and uncertainty. A container marker without a finite cgroup value does not imply a finite limit.

Raw lines 199 to 217
218## Disk
220Capacity, free blocks, user-writable blocks, path permission, filesystem quota,
221and actual ability to complete a write are different:
223- capacity is the filesystem's total size;
224- POSIX `f_bfree` includes blocks reserved from an unprivileged user;
225- `shutil.disk_usage().free` uses `f_bavail` on POSIX, so the detector reads
226 `statvfs` to keep filesystem free and user-available values separate;
227- POSIX `f_bavail` estimates blocks available to the current user;
228- `os.access(..., os.W_OK)` is a non-writing permission check, not proof that a
229 future write will succeed;
230- project/user quotas and remote storage policies can be stricter than block
231 counts.
233The detector does not create a probe file. It redacts the absolute working path
234and labels the scope as the working filesystem.

Disk

Capacity, free blocks, user-writable blocks, path permission, filesystem quota, and actual ability to complete a write are different:

  • capacity is the filesystem's total size;
  • POSIX f_bfree includes blocks reserved from an unprivileged user;
  • shutil.disk_usage().free uses f_bavail on POSIX, so the detector reads statvfs to keep filesystem free and user-available values separate;
  • POSIX f_bavail estimates blocks available to the current user;
  • os.access(..., os.W_OK) is a non-writing permission check, not proof that a future write will succeed;
  • project/user quotas and remote storage policies can be stricter than block counts.

The detector does not create a probe file. It redacts the absolute working path and labels the scope as the working filesystem.

Raw lines 218 to 234